OSDN Git Service

Merge tag 'regulator-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Dec 2018 22:38:31 +0000 (14:38 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Tue, 25 Dec 2018 22:38:31 +0000 (14:38 -0800)
Pull regulator updates from Mark Brown:
 "This has been a very busy release for the core, some fixes, one large
  new feature and a big bit of refactoring to update the GPIO API:

   - Support for coupled regulators from Dmitry Osipenko based on a
     prior attempt by Maciej Purski, allowing us to handle situations
     where the voltages on two regulators can't be too far apart from
     each other.

   - Conversion of the GPIO support in both drivers and the core to use
     GPIO descriptors rather than numbers, part of the overall project
     to remove GPIO numbers.

   - Support for standby mode suspend states from Andrei Stefanescu.

   - New drivers for Allwinner AXP209, Cirrus Logic Lochnagar and
     Microchip MPC16502"

* tag 'regulator-v4.21' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (90 commits)
  regulator: tps65910: fix a missing check of return value
  regulator: mcp16502: Select REGMAP_I2C to fix build error
  regulator: convert to DEFINE_SHOW_ATTRIBUTE
  regulator: mcp16502: Fix missing n_voltages setting
  regulator: mcp16502: Use #ifdef CONFIG_PM_SLEEP around mcp16502_suspend/resume_noirq
  regulator: mcp16502: code cleanup
  regulator: act8945a-regulator: make symbol act8945a_pm static
  drivers/regulator: fix a missing check of return value
  regulator: act8945a-regulator: fix 'defined but not used' compiler warning
  regulator: axp20x: fix set_ramp_delay for AXP209/dcdc2
  regulator: mcp16502: add support for suspend
  mfd: axp20x: use explicit bit defines
  mfd: axp20x: Clean up included headers
  regulator: dts: enable soft-start and ramp delay for the OLinuXino Lime2
  dt-bindings: mfd: axp20x: Add software based soft_start for AXP209 LDO3
  regulator: axp20x: add software based soft_start for AXP209 LDO3
  dt-bindings: mfd: axp20x: add support for regulator-ramp-delay for AXP209
  regulator: axp20x: add support for set_ramp_delay for AXP209
  mfd: axp20x: name voltage ramping define properly
  regulator: mcp16502: add regulator driver for MCP16502
  ...

59 files changed:
Documentation/devicetree/bindings/mfd/axp20x.txt
Documentation/devicetree/bindings/regulator/act8945a-regulator.txt
Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/regulator.txt
Documentation/driver-model/devres.txt
MAINTAINERS
arch/arm/boot/dts/sun7i-a20-olinuxino-lime2.dts
arch/arm/mach-s3c64xx/mach-crag6410-module.c
drivers/gpio/gpiolib-devres.c
drivers/gpio/gpiolib.c
drivers/gpio/gpiolib.h
drivers/mfd/axp20x.c
drivers/mfd/wm8994-core.c
drivers/regulator/88pm8607.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/act8945a-regulator.c
drivers/regulator/arizona-ldo1.c
drivers/regulator/as3711-regulator.c
drivers/regulator/axp20x-regulator.c
drivers/regulator/bd718x7-regulator.c
drivers/regulator/bd9571mwv-regulator.c
drivers/regulator/core.c
drivers/regulator/da9052-regulator.c
drivers/regulator/da9210-regulator.c
drivers/regulator/da9211-regulator.c
drivers/regulator/dbx500-prcmu.c
drivers/regulator/fixed.c
drivers/regulator/internal.h
drivers/regulator/lm363x-regulator.c
drivers/regulator/lochnagar-regulator.c
drivers/regulator/lp8788-ldo.c
drivers/regulator/max77686-regulator.c
drivers/regulator/max8952.c
drivers/regulator/max8973-regulator.c
drivers/regulator/max8997-regulator.c
drivers/regulator/mc13xxx-regulator-core.c
drivers/regulator/mcp16502.c [new file with mode: 0644]
drivers/regulator/of_regulator.c
drivers/regulator/palmas-regulator.c
drivers/regulator/pfuze100-regulator.c
drivers/regulator/qcom-rpmh-regulator.c
drivers/regulator/s2mps11.c
drivers/regulator/s5m8767.c
drivers/regulator/stpmic1_regulator.c
drivers/regulator/tps65090-regulator.c
drivers/regulator/tps65910-regulator.c
drivers/regulator/wm8350-regulator.c
drivers/regulator/wm8994-regulator.c
drivers/spi/spi-qcom-qspi.c
include/dt-bindings/regulator/active-semi,8945a-regulator.h [new file with mode: 0644]
include/linux/gpio/consumer.h
include/linux/mfd/axp20x.h
include/linux/mfd/wm8994/pdata.h
include/linux/regulator/consumer.h
include/linux/regulator/driver.h
include/linux/regulator/machine.h
include/linux/regulator/pfuze100.h

index 188f037..2af4ff9 100644 (file)
@@ -32,6 +32,15 @@ Required properties:
 - interrupt-controller: The PMIC has its own internal IRQs
 - #interrupt-cells: Should be set to 1
 
+Supported common regulator properties, see ../regulator/regulator.txt for
+more information:
+- regulator-ramp-delay: sets the ramp up delay in uV/us
+                       AXP20x/DCDC2: 1600, 800
+                       AXP20x/LDO3:  1600, 800
+- regulator-soft-start:        enable the output at the lowest possible voltage and
+                       only then set the desired voltage
+                       AXP20x/LDO3: software-based implementation
+
 Optional properties:
 - x-powers,dcdc-freq: defines the work frequency of DC-DC in KHz
                      AXP152/20X: range:  750-1875, Default: 1.5 MHz
index ac955de..4017527 100644 (file)
@@ -15,11 +15,17 @@ Optional input supply properties:
   - inl67-supply: The input supply for REG_LDO3 and REG_LDO4
 
 Any standard regulator properties can be used to configure the single regulator.
+regulator-initial-mode, regulator-allowed-modes and regulator-mode could be
+specified using mode values from dt-bindings/regulator/active-semi,8945a-regulator.h
+file.
 
 The valid names for regulators are:
        REG_DCDC1, REG_DCDC2, REG_DCDC3, REG_LDO1, REG_LDO2, REG_LDO3, REG_LDO4.
 
 Example:
+
+#include <dt-bindings/regulator/active-semi,8945a-regulator.h>
+
        pmic@5b {
                compatible = "active-semi,act8945a";
                reg = <0x5b>;
@@ -32,6 +38,18 @@ Example:
                                regulator-min-microvolt = <1350000>;
                                regulator-max-microvolt = <1350000>;
                                regulator-always-on;
+
+                               regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_FIXED>,
+                                                         <ACT8945A_REGULATOR_MODE_LOWPOWER>;
+                               regulator-initial-mode = <ACT8945A_REGULATOR_MODE_FIXED>;
+
+                               regulator-state-mem {
+                                       regulator-on-in-suspend;
+                                       regulator-suspend-min-microvolt=<1400000>;
+                                       regulator-suspend-max-microvolt=<1400000>;
+                                       regulator-changeable-in-suspend;
+                                       regulator-mode=<ACT8945A_REGULATOR_MODE_LOWPOWER>;
+                               };
                        };
 
                        vdd_1v2_reg: REG_DCDC2 {
@@ -39,6 +57,14 @@ Example:
                                regulator-min-microvolt = <1100000>;
                                regulator-max-microvolt = <1300000>;
                                regulator-always-on;
+
+                               regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_FIXED>,
+                                                         <ACT8945A_REGULATOR_MODE_LOWPOWER>;
+                               regulator-initial-mode = <ACT8945A_REGULATOR_MODE_FIXED>;
+
+                               regulator-state-mem {
+                                       regulator-off-in-suspend;
+                               };
                        };
 
                        vdd_3v3_reg: REG_DCDC3 {
@@ -53,6 +79,14 @@ Example:
                                regulator-min-microvolt = <2500000>;
                                regulator-max-microvolt = <2500000>;
                                regulator-always-on;
+
+                               regulator-allowed-modes = <ACT8945A_REGULATOR_MODE_NORMAL>,
+                                                         <ACT8945A_REGULATOR_MODE_LOWPOWER>;
+                               regulator-initial-mode = <ACT8945A_REGULATOR_MODE_NORMAL>;
+
+                               regulator-state-mem {
+                                       regulator-off-in-suspend;
+                               };
                        };
 
                        vdd_3v3_lp_reg: REG_LDO2 {
diff --git a/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt b/Documentation/devicetree/bindings/regulator/cirrus,lochnagar.txt
new file mode 100644 (file)
index 0000000..91974e6
--- /dev/null
@@ -0,0 +1,82 @@
+Cirrus Logic Lochnagar Audio Development Board
+
+Lochnagar is an evaluation and development board for Cirrus Logic
+Smart CODEC and Amp devices. It allows the connection of most Cirrus
+Logic devices on mini-cards, as well as allowing connection of
+various application processor systems to provide a full evaluation
+platform.  Audio system topology, clocking and power can all be
+controlled through the Lochnagar, allowing the device under test
+to be used in a variety of possible use cases.
+
+This binding document describes the binding for the regulator portion
+of the driver.
+
+Also see these documents for generic binding information:
+  [1] Regulator: ../regulator/regulator.txt
+
+This binding must be part of the Lochnagar MFD binding:
+  [2] ../mfd/cirrus,lochnagar.txt
+
+Optional sub-nodes:
+
+  - VDDCORE : Initialisation data for the VDDCORE regulator, which
+    supplies the CODECs digital core if it has no build regulator for that
+    purpose.
+      Required Properties:
+      - compatible : One of the following strings:
+                     "cirrus,lochnagar2-vddcore"
+      - SYSVDD-supply: Primary power supply for the Lochnagar.
+
+  - MICVDD : Initialisation data for the MICVDD regulator, which
+    supplies the CODECs MICVDD.
+      Required Properties:
+      - compatible : One of the following strings:
+                     "cirrus,lochnagar2-micvdd"
+      - SYSVDD-supply: Primary power supply for the Lochnagar.
+
+  - MIC1VDD, MIC2VDD : Initialisation data for the MICxVDD supplies.
+      Required Properties:
+      - compatible : One of the following strings:
+                     "cirrus,lochnagar2-mic1vdd", "cirrus,lochnagar2-mic2vdd"
+      Optional Properties:
+      - cirrus,micbias-input : A property selecting which of the CODEC
+        minicard micbias outputs should be used, valid values are 1 - 4.
+      - MICBIAS1-supply, MICBIAS2-supply: Regulator supplies for the
+        MICxVDD outputs, supplying the digital microphones, normally
+        supplied from the attached CODEC.
+
+  - VDD1V8 : Recommended fixed regulator for the VDD1V8 regulator, which supplies the
+    CODECs analog and 1.8V digital supplies.
+      Required Properties:
+      - compatible : Should be set to "regulator-fixed"
+      - regulator-min-microvolt : Should be set to 1.8V
+      - regulator-max-microvolt : Should be set to 1.8V
+      - regulator-boot-on
+      - regulator-always-on
+      - vin-supply : Should be set to same supply as SYSVDD
+
+Example:
+
+lochnagar {
+       lochnagar-micvdd: MICVDD {
+               compatible = "cirrus,lochnagar2-micvdd";
+
+               SYSVDD-supply = <&wallvdd>;
+
+               regulator-min-microvolt = <3300000>;
+               regulator-max-microvolt = <3300000>;
+       };
+
+       lochnagar-vdd1v8: VDD1V8 {
+               compatible = "regulator-fixed";
+
+               regulator-name = "VDD1V8";
+               regulator-min-microvolt = <1800000>;
+               regulator-max-microvolt = <1800000>;
+               regulator-boot-on;
+               regulator-always-on;
+
+               vin-supply = <&wallvdd>;
+       };
+};
+
diff --git a/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt b/Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
new file mode 100644 (file)
index 0000000..b8f843f
--- /dev/null
@@ -0,0 +1,143 @@
+MCP16502 PMIC
+
+Required properties:
+- compatible: "microchip,mcp16502"
+- reg: I2C slave address
+- lpm-gpios: GPIO for LPM pin. Note that this GPIO *must* remain high during
+            suspend-to-ram, keeping the PMIC into HIBERNATE mode.
+- regulators: A node that houses a sub-node for each regulator within
+              the device. Each sub-node is identified using the node's
+              name. The content of each sub-node is defined by the
+              standard binding for regulators; see regulator.txt.
+
+Regualtors of MCP16502 PMIC:
+1) VDD_IO      - Buck (1.2 - 3.7 V)
+2) VDD_DDR     - Buck (0.6 - 1.85 V)
+3) VDD_CORE    - Buck (0.6 - 1.85 V)
+4) VDD_OTHER   - BUCK (0.6 - 1.85 V)
+5) LDO1                - LDO  (1.2 - 3.7 V)
+6) LDO2                - LDO  (1.2 - 3.7 V)
+
+Regulator modes:
+2 - FPWM: higher precision, higher consumption
+4 - AutoPFM: lower precision, lower consumption
+
+Each regulator is defined using the standard binding for regulators.
+
+Example:
+
+mcp16502@5b {
+       compatible = "microchip,mcp16502";
+       reg = <0x5b>;
+       status = "okay";
+       lpm-gpios = <&pioBU 7 GPIO_ACTIVE_HIGH>;
+
+       regulators {
+               VDD_IO {
+                       regulator-name = "VDD_IO";
+                       regulator-min-microvolt = <1200000>;
+                       regulator-max-microvolt = <3700000>;
+                       regulator-initial-mode = <2>;
+                       regulator-allowed-modes = <2>, <4>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                               regulator-mode = <4>;
+                       };
+
+                       regulator-state-mem {
+                               regulator-off-in-suspend;
+                               regulator-mode = <4>;
+                       };
+               };
+
+               VDD_DDR {
+                       regulator-name = "VDD_DDR";
+                       regulator-min-microvolt = <600000>;
+                       regulator-max-microvolt = <1850000>;
+                       regulator-initial-mode = <2>;
+                       regulator-allowed-modes = <2>, <4>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                               regulator-mode = <4>;
+                       };
+
+                       regulator-state-mem {
+                               regulator-on-in-suspend;
+                               regulator-mode = <4>;
+                       };
+               };
+
+               VDD_CORE {
+                       regulator-name = "VDD_CORE";
+                       regulator-min-microvolt = <600000>;
+                       regulator-max-microvolt = <1850000>;
+                       regulator-initial-mode = <2>;
+                       regulator-allowed-modes = <2>, <4>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                               regulator-mode = <4>;
+                       };
+
+                       regulator-state-mem {
+                               regulator-off-in-suspend;
+                               regulator-mode = <4>;
+                       };
+               };
+
+               VDD_OTHER {
+                       regulator-name = "VDD_OTHER";
+                       regulator-min-microvolt = <600000>;
+                       regulator-max-microvolt = <1850000>;
+                       regulator-initial-mode = <2>;
+                       regulator-allowed-modes = <2>, <4>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                               regulator-mode = <4>;
+                       };
+
+                       regulator-state-mem {
+                               regulator-off-in-suspend;
+                               regulator-mode = <4>;
+                       };
+               };
+
+               LDO1 {
+                       regulator-name = "LDO1";
+                       regulator-min-microvolt = <1200000>;
+                       regulator-max-microvolt = <3700000>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                       };
+
+                       regulator-state-mem {
+                               regulator-off-in-suspend;
+                       };
+               };
+
+               LDO2 {
+                       regulator-name = "LDO2";
+                       regulator-min-microvolt = <1200000>;
+                       regulator-max-microvolt = <3700000>;
+                       regulator-always-on;
+
+                       regulator-state-standby {
+                               regulator-on-in-suspend;
+                       };
+
+                       regulator-state-mem {
+                               regulator-off-in-suspend;
+                       };
+               };
+
+       };
+};
index a7cd368..0a3f087 100644 (file)
@@ -33,13 +33,16 @@ Optional properties:
   decreases of any level. This is useful for regulators with exponential
   voltage changes.
 - regulator-soft-start: Enable soft start so that voltage ramps slowly
+- regulator-state-standby sub-root node for Standby mode
+  : equivalent with standby Linux sleep state, which provides energy savings
+  with a relatively quick transition back time.
 - regulator-state-mem sub-root node for Suspend-to-RAM mode
   : suspend to memory, the device goes to sleep, but all data stored in memory,
   only some external interrupt can wake the device.
 - regulator-state-disk sub-root node for Suspend-to-DISK mode
   : suspend to disk, this state operates similarly to Suspend-to-RAM,
   but includes a final step of writing memory contents to disk.
-- regulator-state-[mem/disk] node has following common properties:
+- regulator-state-[mem/disk/standby] 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-min-microvolt: minimum voltage may be set in
@@ -76,8 +79,11 @@ Optional properties:
 - regulator-coupled-with: Regulators with which the regulator
   is coupled. The linkage is 2-way - all coupled regulators should be linked
   with each other. A regulator should not be coupled with its supplier.
-- regulator-coupled-max-spread: Max spread between voltages of coupled regulators
-  in microvolts.
+- regulator-coupled-max-spread: Array of maximum spread between voltages of
+  coupled regulators in microvolts, each value in the array relates to the
+  corresponding couple specified by the regulator-coupled-with property.
+- regulator-max-step-microvolt: Maximum difference between current and target
+  voltages that can be changed safely in a single step.
 
 Deprecated properties:
 - regulator-compatible: If a regulator chip contains multiple
index 43681ca..fc4cc24 100644 (file)
@@ -254,6 +254,7 @@ GPIO
   devm_gpiod_get_index_optional()
   devm_gpiod_get_optional()
   devm_gpiod_put()
+  devm_gpiod_unhinge()
   devm_gpiochip_add_data()
   devm_gpiochip_remove()
   devm_gpio_request()
index 7808b16..f0f0c23 100644 (file)
@@ -9891,6 +9891,13 @@ M:       Ludovic Desroches <ludovic.desroches@microchip.com>
 S:     Maintained
 F:     drivers/mmc/host/atmel-mci.c
 
+MICROCHIP MCP16502 PMIC DRIVER
+M:     Andrei Stefanescu <andrei.stefanescu@microchip.com>
+L:     linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
+S:     Maintained
+F:     Documentation/devicetree/bindings/regulator/mcp16502-regulator.txt
+F:     drivers/regulator/mcp16502.c
+
 MICROCHIP MCP3911 ADC DRIVER
 M:     Marcus Folkesson <marcus.folkesson@gmail.com>
 M:     Kent Gustavsson <kent@minoris.se>
index b828677..ffafe97 100644 (file)
        regulator-min-microvolt = <2800000>;
        regulator-max-microvolt = <2800000>;
        regulator-name = "vddio-csi0";
+       regulator-soft-start;
+       regulator-ramp-delay = <1600>;
 };
 
 &reg_ldo4 {
index 5aa4728..76c4855 100644 (file)
@@ -194,8 +194,8 @@ static struct wm8994_pdata wm8994_pdata = {
                0x3,          /* IRQ out, active high, CMOS */
        },
        .ldo = {
-                { .enable = S3C64XX_GPN(6), .init_data = &wm8994_ldo1, },
-                { .enable = S3C64XX_GPN(4), .init_data = &wm8994_ldo2, },
+                { .init_data = &wm8994_ldo1, },
+                { .init_data = &wm8994_ldo2, },
        },
 };
 
@@ -203,6 +203,18 @@ static const struct i2c_board_info wm1277_devs[] = {
        { I2C_BOARD_INFO("wm8958", 0x1a),  /* WM8958 is the superset */
          .platform_data = &wm8994_pdata,
          .irq = GLENFARCLAS_PMIC_IRQ_BASE + WM831X_IRQ_GPIO_2,
+         .dev_name = "wm8958",
+       },
+};
+
+static struct gpiod_lookup_table wm8994_gpiod_table = {
+       .dev_id = "i2c-wm8958", /* I2C device name */
+       .table = {
+               GPIO_LOOKUP("GPION", 6,
+                           "wlf,ldo1ena", GPIO_ACTIVE_HIGH),
+               GPIO_LOOKUP("GPION", 4,
+                           "wlf,ldo2ena", GPIO_ACTIVE_HIGH),
+               { },
        },
 };
 
@@ -381,6 +393,7 @@ static int wlf_gf_module_probe(struct i2c_client *i2c,
 
        gpiod_add_lookup_table(&wm5102_reva_gpiod_table);
        gpiod_add_lookup_table(&wm5102_gpiod_table);
+       gpiod_add_lookup_table(&wm8994_gpiod_table);
 
        if (i < ARRAY_SIZE(gf_mods)) {
                dev_info(&i2c->dev, "%s revision %d\n",
index 0195936..0acc2cc 100644 (file)
@@ -98,15 +98,28 @@ struct gpio_desc *__must_check devm_gpiod_get_index(struct device *dev,
        struct gpio_desc **dr;
        struct gpio_desc *desc;
 
+       desc = gpiod_get_index(dev, con_id, idx, flags);
+       if (IS_ERR(desc))
+               return desc;
+
+       /*
+        * For non-exclusive GPIO descriptors, check if this descriptor is
+        * already under resource management by this device.
+        */
+       if (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) {
+               struct devres *dres;
+
+               dres = devres_find(dev, devm_gpiod_release,
+                                  devm_gpiod_match, &desc);
+               if (dres)
+                       return desc;
+       }
+
        dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
                          GFP_KERNEL);
-       if (!dr)
+       if (!dr) {
+               gpiod_put(desc);
                return ERR_PTR(-ENOMEM);
-
-       desc = gpiod_get_index(dev, con_id, idx, flags);
-       if (IS_ERR(desc)) {
-               devres_free(dr);
-               return desc;
        }
 
        *dr = desc;
@@ -140,15 +153,28 @@ struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
        struct gpio_desc **dr;
        struct gpio_desc *desc;
 
+       desc = gpiod_get_from_of_node(node, propname, index, dflags, label);
+       if (IS_ERR(desc))
+               return desc;
+
+       /*
+        * For non-exclusive GPIO descriptors, check if this descriptor is
+        * already under resource management by this device.
+        */
+       if (dflags & GPIOD_FLAGS_BIT_NONEXCLUSIVE) {
+               struct devres *dres;
+
+               dres = devres_find(dev, devm_gpiod_release,
+                                  devm_gpiod_match, &desc);
+               if (dres)
+                       return desc;
+       }
+
        dr = devres_alloc(devm_gpiod_release, sizeof(struct gpio_desc *),
                          GFP_KERNEL);
-       if (!dr)
+       if (!dr) {
+               gpiod_put(desc);
                return ERR_PTR(-ENOMEM);
-
-       desc = gpiod_get_from_of_node(node, propname, index, dflags, label);
-       if (IS_ERR(desc)) {
-               devres_free(dr);
-               return desc;
        }
 
        *dr = desc;
@@ -321,6 +347,36 @@ void devm_gpiod_put(struct device *dev, struct gpio_desc *desc)
 EXPORT_SYMBOL(devm_gpiod_put);
 
 /**
+ * devm_gpiod_unhinge - Remove resource management from a gpio descriptor
+ * @dev:       GPIO consumer
+ * @desc:      GPIO descriptor to remove resource management from
+ *
+ * Remove resource management from a GPIO descriptor. This is needed when
+ * you want to hand over lifecycle management of a descriptor to another
+ * mechanism.
+ */
+
+void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc)
+{
+       int ret;
+
+       if (IS_ERR_OR_NULL(desc))
+               return;
+       ret = devres_destroy(dev, devm_gpiod_release,
+                            devm_gpiod_match, &desc);
+       /*
+        * If the GPIO descriptor is requested as nonexclusive, we
+        * may call this function several times on the same descriptor
+        * so it is OK if devres_destroy() returns -ENOENT.
+        */
+       if (ret == -ENOENT)
+               return;
+       /* Anything else we should warn about */
+       WARN_ON(ret);
+}
+EXPORT_SYMBOL(devm_gpiod_unhinge);
+
+/**
  * devm_gpiod_put_array - Resource-managed gpiod_put_array()
  * @dev:       GPIO consumer
  * @descs:     GPIO descriptor array to dispose of
index a2cbb47..985c09c 100644 (file)
@@ -4205,6 +4205,8 @@ struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
        transitory = flags & OF_GPIO_TRANSITORY;
 
        ret = gpiod_request(desc, label);
+       if (ret == -EBUSY && (flags & GPIOD_FLAGS_BIT_NONEXCLUSIVE))
+               return desc;
        if (ret)
                return ERR_PTR(ret);
 
index 087d865..bc57f0d 100644 (file)
@@ -201,12 +201,6 @@ int gpiod_set_array_value_complex(bool raw, bool can_sleep,
                                  struct gpio_array *array_info,
                                  unsigned long *value_bitmap);
 
-/* This is just passed between gpiolib and devres */
-struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
-                                        const char *propname, int index,
-                                        enum gpiod_flags dflags,
-                                        const char *label);
-
 extern struct spinlock gpio_lock;
 extern struct list_head gpio_devices;
 
index 0be511d..e1450a5 100644 (file)
  * published by the Free Software Foundation.
  */
 
-#include <linux/err.h>
+#include <linux/acpi.h>
+#include <linux/bitops.h>
 #include <linux/delay.h>
+#include <linux/err.h>
 #include <linux/interrupt.h>
 #include <linux/kernel.h>
+#include <linux/mfd/axp20x.h>
+#include <linux/mfd/core.h>
 #include <linux/module.h>
+#include <linux/of_device.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
-#include <linux/mfd/axp20x.h>
-#include <linux/mfd/core.h>
-#include <linux/of_device.h>
-#include <linux/acpi.h>
 
-#define AXP20X_OFF     0x80
+#define AXP20X_OFF     BIT(7)
 
 #define AXP806_REG_ADDR_EXT_ADDR_MASTER_MODE   0
 #define AXP806_REG_ADDR_EXT_ADDR_SLAVE_MODE    BIT(4)
index 22bd652..04a177e 100644 (file)
@@ -21,7 +21,6 @@
 #include <linux/mfd/core.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
-#include <linux/of_gpio.h>
 #include <linux/pm_runtime.h>
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
@@ -306,14 +305,6 @@ static int wm8994_set_pdata_from_of(struct wm8994 *wm8994)
 
        pdata->csnaddr_pd = of_property_read_bool(np, "wlf,csnaddr-pd");
 
-       pdata->ldo[0].enable = of_get_named_gpio(np, "wlf,ldo1ena", 0);
-       if (pdata->ldo[0].enable < 0)
-               pdata->ldo[0].enable = 0;
-
-       pdata->ldo[1].enable = of_get_named_gpio(np, "wlf,ldo2ena", 0);
-       if (pdata->ldo[1].enable < 0)
-               pdata->ldo[1].enable = 0;
-
        return 0;
 }
 #else
index fd86446..28f5524 100644 (file)
@@ -328,7 +328,7 @@ static int pm8607_regulator_dt_init(struct platform_device *pdev,
                return -ENODEV;
        }
        for_each_child_of_node(nproot, np) {
-               if (!of_node_cmp(np->name, info->desc.name)) {
+               if (of_node_name_eq(np, info->desc.name)) {
                        config->init_data =
                                of_get_regulator_init_data(&pdev->dev, np,
                                                           &info->desc);
index 926cee0..ee60a22 100644 (file)
@@ -567,6 +567,16 @@ config REGULATOR_MC13892
          Say y here to support the regulators found on the Freescale MC13892
          PMIC.
 
+config REGULATOR_MCP16502
+       tristate "Microchip MCP16502 PMIC"
+       depends on I2C && OF
+       select REGMAP_I2C
+       help
+         Say y here to support the MCP16502 PMIC. This driver supports
+         basic operations (get/set voltage, get/set operating mode)
+         through the regulator interface. In addition it enables
+         suspend-to-ram/standby transition.
+
 config REGULATOR_MT6311
        tristate "MediaTek MT6311 PMIC"
        depends on I2C
index 72488ef..b12e1c9 100644 (file)
@@ -74,6 +74,7 @@ obj-$(CONFIG_REGULATOR_MAX77802) += max77802-regulator.o
 obj-$(CONFIG_REGULATOR_MC13783) += mc13783-regulator.o
 obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
+obj-$(CONFIG_REGULATOR_MCP16502) += mcp16502.o
 obj-$(CONFIG_REGULATOR_MT6311) += mt6311-regulator.o
 obj-$(CONFIG_REGULATOR_MT6323) += mt6323-regulator.o
 obj-$(CONFIG_REGULATOR_MT6380) += mt6380-regulator.o
index 43fda8b..603db77 100644 (file)
 #include <linux/module.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
+#include <linux/regmap.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
+#include <dt-bindings/regulator/active-semi,8945a-regulator.h>
 
 /**
  * ACT8945A Global Register Map.
  */
 #define ACT8945A_SYS_MODE      0x00
 #define ACT8945A_SYS_CTRL      0x01
+#define ACT8945A_SYS_UNLK_REGS 0x0b
 #define ACT8945A_DCDC1_VSET1   0x20
 #define ACT8945A_DCDC1_VSET2   0x21
 #define ACT8945A_DCDC1_CTRL    0x22
+#define ACT8945A_DCDC1_SUS     0x24
 #define ACT8945A_DCDC2_VSET1   0x30
 #define ACT8945A_DCDC2_VSET2   0x31
 #define ACT8945A_DCDC2_CTRL    0x32
+#define ACT8945A_DCDC2_SUS     0x34
 #define ACT8945A_DCDC3_VSET1   0x40
 #define ACT8945A_DCDC3_VSET2   0x41
 #define ACT8945A_DCDC3_CTRL    0x42
+#define ACT8945A_DCDC3_SUS     0x44
 #define ACT8945A_LDO1_VSET     0x50
 #define ACT8945A_LDO1_CTRL     0x51
+#define ACT8945A_LDO1_SUS      0x52
 #define ACT8945A_LDO2_VSET     0x54
 #define ACT8945A_LDO2_CTRL     0x55
+#define ACT8945A_LDO2_SUS      0x56
 #define ACT8945A_LDO3_VSET     0x60
 #define ACT8945A_LDO3_CTRL     0x61
+#define ACT8945A_LDO3_SUS      0x62
 #define ACT8945A_LDO4_VSET     0x64
 #define ACT8945A_LDO4_CTRL     0x65
+#define ACT8945A_LDO4_SUS      0x66
 
 /**
  * Field Definitions.
@@ -60,7 +70,12 @@ enum {
        ACT8945A_ID_LDO2,
        ACT8945A_ID_LDO3,
        ACT8945A_ID_LDO4,
-       ACT8945A_REG_NUM,
+       ACT8945A_ID_MAX,
+};
+
+struct act8945a_pmic {
+       struct regmap *regmap;
+       u32 op_mode[ACT8945A_ID_MAX];
 };
 
 static const struct regulator_linear_range act8945a_voltage_ranges[] = {
@@ -69,6 +84,143 @@ static const struct regulator_linear_range act8945a_voltage_ranges[] = {
        REGULATOR_LINEAR_RANGE(2400000, 48, 63, 100000),
 };
 
+static int act8945a_set_suspend_state(struct regulator_dev *rdev, bool enable)
+{
+       struct regmap *regmap = rdev->regmap;
+       int id = rdev->desc->id, reg, val;
+
+       switch (id) {
+       case ACT8945A_ID_DCDC1:
+               reg = ACT8945A_DCDC1_SUS;
+               val = 0xa8;
+               break;
+       case ACT8945A_ID_DCDC2:
+               reg = ACT8945A_DCDC2_SUS;
+               val = 0xa8;
+               break;
+       case ACT8945A_ID_DCDC3:
+               reg = ACT8945A_DCDC3_SUS;
+               val = 0xa8;
+               break;
+       case ACT8945A_ID_LDO1:
+               reg = ACT8945A_LDO1_SUS;
+               val = 0xe8;
+               break;
+       case ACT8945A_ID_LDO2:
+               reg = ACT8945A_LDO2_SUS;
+               val = 0xe8;
+               break;
+       case ACT8945A_ID_LDO3:
+               reg = ACT8945A_LDO3_SUS;
+               val = 0xe8;
+               break;
+       case ACT8945A_ID_LDO4:
+               reg = ACT8945A_LDO4_SUS;
+               val = 0xe8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (enable)
+               val |= BIT(4);
+
+       /*
+        * Ask the PMIC to enable/disable this output when entering hibernate
+        * mode.
+        */
+       return regmap_write(regmap, reg, val);
+}
+
+static int act8945a_set_suspend_enable(struct regulator_dev *rdev)
+{
+       return act8945a_set_suspend_state(rdev, true);
+}
+
+static int act8945a_set_suspend_disable(struct regulator_dev *rdev)
+{
+       return act8945a_set_suspend_state(rdev, false);
+}
+
+static unsigned int act8945a_of_map_mode(unsigned int mode)
+{
+       switch (mode) {
+       case ACT8945A_REGULATOR_MODE_FIXED:
+       case ACT8945A_REGULATOR_MODE_NORMAL:
+               return REGULATOR_MODE_NORMAL;
+       case ACT8945A_REGULATOR_MODE_LOWPOWER:
+               return REGULATOR_MODE_STANDBY;
+       default:
+               return REGULATOR_MODE_INVALID;
+       }
+}
+
+static int act8945a_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev);
+       struct regmap *regmap = rdev->regmap;
+       int id = rdev->desc->id;
+       int reg, ret, val = 0;
+
+       switch (id) {
+       case ACT8945A_ID_DCDC1:
+               reg = ACT8945A_DCDC1_CTRL;
+               break;
+       case ACT8945A_ID_DCDC2:
+               reg = ACT8945A_DCDC2_CTRL;
+               break;
+       case ACT8945A_ID_DCDC3:
+               reg = ACT8945A_DCDC3_CTRL;
+               break;
+       case ACT8945A_ID_LDO1:
+               reg = ACT8945A_LDO1_SUS;
+               break;
+       case ACT8945A_ID_LDO2:
+               reg = ACT8945A_LDO2_SUS;
+               break;
+       case ACT8945A_ID_LDO3:
+               reg = ACT8945A_LDO3_SUS;
+               break;
+       case ACT8945A_ID_LDO4:
+               reg = ACT8945A_LDO4_SUS;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (mode) {
+       case REGULATOR_MODE_STANDBY:
+               if (rdev->desc->id > ACT8945A_ID_DCDC3)
+                       val = BIT(5);
+               break;
+       case REGULATOR_MODE_NORMAL:
+               if (rdev->desc->id <= ACT8945A_ID_DCDC3)
+                       val = BIT(5);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = regmap_update_bits(regmap, reg, BIT(5), val);
+       if (ret)
+               return ret;
+
+       act8945a->op_mode[id] = mode;
+
+       return 0;
+}
+
+static unsigned int act8945a_get_mode(struct regulator_dev *rdev)
+{
+       struct act8945a_pmic *act8945a = rdev_get_drvdata(rdev);
+       int id = rdev->desc->id;
+
+       if (id < ACT8945A_ID_DCDC1 || id >= ACT8945A_ID_MAX)
+               return -EINVAL;
+
+       return act8945a->op_mode[id];
+}
+
 static const struct regulator_ops act8945a_ops = {
        .list_voltage           = regulator_list_voltage_linear_range,
        .map_voltage            = regulator_map_voltage_linear_range,
@@ -76,7 +228,11 @@ static const struct regulator_ops act8945a_ops = {
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
+       .set_mode               = act8945a_set_mode,
+       .get_mode               = act8945a_get_mode,
        .is_enabled             = regulator_is_enabled_regmap,
+       .set_suspend_enable     = act8945a_set_suspend_enable,
+       .set_suspend_disable    = act8945a_set_suspend_disable,
 };
 
 #define ACT89xx_REG(_name, _family, _id, _vsel_reg, _supply)           \
@@ -84,6 +240,7 @@ static const struct regulator_ops act8945a_ops = {
                .name                   = _name,                        \
                .supply_name            = _supply,                      \
                .of_match               = of_match_ptr("REG_"#_id),     \
+               .of_map_mode            = act8945a_of_map_mode,         \
                .regulators_node        = of_match_ptr("regulators"),   \
                .id                     = _family##_ID_##_id,           \
                .type                   = REGULATOR_VOLTAGE,            \
@@ -122,10 +279,22 @@ static int act8945a_pmic_probe(struct platform_device *pdev)
 {
        struct regulator_config config = { };
        const struct regulator_desc *regulators;
+       struct act8945a_pmic *act8945a;
        struct regulator_dev *rdev;
        int i, num_regulators;
        bool voltage_select;
 
+       act8945a = devm_kzalloc(&pdev->dev, sizeof(*act8945a), GFP_KERNEL);
+       if (!act8945a)
+               return -ENOMEM;
+
+       act8945a->regmap = dev_get_regmap(pdev->dev.parent, NULL);
+       if (!act8945a->regmap) {
+               dev_err(&pdev->dev,
+                       "could not retrieve regmap from parent device\n");
+               return -EINVAL;
+       }
+
        voltage_select = of_property_read_bool(pdev->dev.parent->of_node,
                                               "active-semi,vsel-high");
 
@@ -139,8 +308,10 @@ static int act8945a_pmic_probe(struct platform_device *pdev)
 
        config.dev = &pdev->dev;
        config.dev->of_node = pdev->dev.parent->of_node;
+       config.driver_data = act8945a;
        for (i = 0; i < num_regulators; i++) {
-               rdev = devm_regulator_register(&pdev->dev, &regulators[i], &config);
+               rdev = devm_regulator_register(&pdev->dev, &regulators[i],
+                                              &config);
                if (IS_ERR(rdev)) {
                        dev_err(&pdev->dev,
                                "failed to register %s regulator\n",
@@ -149,14 +320,42 @@ static int act8945a_pmic_probe(struct platform_device *pdev)
                }
        }
 
-       return 0;
+       platform_set_drvdata(pdev, act8945a);
+
+       /* Unlock expert registers. */
+       return regmap_write(act8945a->regmap, ACT8945A_SYS_UNLK_REGS, 0xef);
+}
+
+static int __maybe_unused act8945a_suspend(struct device *pdev)
+{
+       struct act8945a_pmic *act8945a = dev_get_drvdata(pdev);
+
+       /*
+        * Ask the PMIC to enter the suspend mode on the next PWRHLD
+        * transition.
+        */
+       return regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x42);
+}
+
+static SIMPLE_DEV_PM_OPS(act8945a_pm, act8945a_suspend, NULL);
+
+static void act8945a_pmic_shutdown(struct platform_device *pdev)
+{
+       struct act8945a_pmic *act8945a = platform_get_drvdata(pdev);
+
+       /*
+        * Ask the PMIC to shutdown everything on the next PWRHLD transition.
+        */
+       regmap_write(act8945a->regmap, ACT8945A_SYS_CTRL, 0x0);
 }
 
 static struct platform_driver act8945a_pmic_driver = {
        .driver = {
                .name = "act8945a-regulator",
+               .pm = &act8945a_pm,
        },
        .probe = act8945a_pmic_probe,
+       .shutdown = act8945a_pmic_shutdown,
 };
 module_platform_driver(act8945a_pmic_driver);
 
index 9a72eae..b9a9304 100644 (file)
@@ -283,9 +283,6 @@ static int arizona_ldo1_common_init(struct platform_device *pdev,
        of_node_put(config.of_node);
 
        if (IS_ERR(ldo1->regulator)) {
-               if (config.ena_gpiod)
-                       gpiod_put(config.ena_gpiod);
-
                ret = PTR_ERR(ldo1->regulator);
                dev_err(&pdev->dev, "Failed to register LDO1 supply: %d\n",
                        ret);
index 565a713..f7fe218 100644 (file)
@@ -1,12 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * AS3711 PMIC regulator driver, using DCDC Step Down and LDO supplies
  *
  * Copyright (C) 2012 Renesas Electronics Corporation
  * Author: Guennadi Liakhovetski, <g.liakhovetski@gmx.de>
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the version 2 of the GNU General Public License as
- * published by the Free Software Foundation
  */
 
 #include <linux/err.h>
index a373403..48af859 100644 (file)
  * GNU General Public License for more details.
  */
 
+#include <linux/bitops.h>
+#include <linux/delay.h>
 #include <linux/err.h>
 #include <linux/init.h>
+#include <linux/mfd/axp20x.h>
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
-#include <linux/mfd/axp20x.h>
 #include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
 
+#define AXP20X_GPIO0_FUNC_MASK         GENMASK(3, 0)
+#define AXP20X_GPIO1_FUNC_MASK         GENMASK(3, 0)
+
 #define AXP20X_IO_ENABLED              0x03
 #define AXP20X_IO_DISABLED             0x07
 
+#define AXP20X_WORKMODE_DCDC2_MASK     BIT_MASK(2)
+#define AXP20X_WORKMODE_DCDC3_MASK     BIT_MASK(1)
+
+#define AXP20X_FREQ_DCDC_MASK          GENMASK(3, 0)
+
+#define AXP20X_VBUS_IPSOUT_MGMT_MASK   BIT_MASK(2)
+
+#define AXP20X_DCDC2_V_OUT_MASK                GENMASK(5, 0)
+#define AXP20X_DCDC3_V_OUT_MASK                GENMASK(7, 0)
+#define AXP20X_LDO24_V_OUT_MASK                GENMASK(7, 4)
+#define AXP20X_LDO3_V_OUT_MASK         GENMASK(6, 0)
+#define AXP20X_LDO5_V_OUT_MASK         GENMASK(7, 4)
+
+#define AXP20X_PWR_OUT_EXTEN_MASK      BIT_MASK(0)
+#define AXP20X_PWR_OUT_DCDC3_MASK      BIT_MASK(1)
+#define AXP20X_PWR_OUT_LDO2_MASK       BIT_MASK(2)
+#define AXP20X_PWR_OUT_LDO4_MASK       BIT_MASK(3)
+#define AXP20X_PWR_OUT_DCDC2_MASK      BIT_MASK(4)
+#define AXP20X_PWR_OUT_LDO3_MASK       BIT_MASK(6)
+
+#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK       BIT_MASK(0)
+#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE(x) \
+       ((x) << 0)
+#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK                BIT_MASK(1)
+#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(x) \
+       ((x) << 1)
+#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK         BIT_MASK(2)
+#define AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN              BIT(2)
+#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK          BIT_MASK(3)
+#define AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN               BIT(3)
+
+#define AXP20X_LDO4_V_OUT_1250mV_START 0x0
+#define AXP20X_LDO4_V_OUT_1250mV_STEPS 0
+#define AXP20X_LDO4_V_OUT_1250mV_END   \
+       (AXP20X_LDO4_V_OUT_1250mV_START + AXP20X_LDO4_V_OUT_1250mV_STEPS)
+#define AXP20X_LDO4_V_OUT_1300mV_START 0x1
+#define AXP20X_LDO4_V_OUT_1300mV_STEPS 7
+#define AXP20X_LDO4_V_OUT_1300mV_END   \
+       (AXP20X_LDO4_V_OUT_1300mV_START + AXP20X_LDO4_V_OUT_1300mV_STEPS)
+#define AXP20X_LDO4_V_OUT_2500mV_START 0x9
+#define AXP20X_LDO4_V_OUT_2500mV_STEPS 0
+#define AXP20X_LDO4_V_OUT_2500mV_END   \
+       (AXP20X_LDO4_V_OUT_2500mV_START + AXP20X_LDO4_V_OUT_2500mV_STEPS)
+#define AXP20X_LDO4_V_OUT_2700mV_START 0xa
+#define AXP20X_LDO4_V_OUT_2700mV_STEPS 1
+#define AXP20X_LDO4_V_OUT_2700mV_END   \
+       (AXP20X_LDO4_V_OUT_2700mV_START + AXP20X_LDO4_V_OUT_2700mV_STEPS)
+#define AXP20X_LDO4_V_OUT_3000mV_START 0xc
+#define AXP20X_LDO4_V_OUT_3000mV_STEPS 3
+#define AXP20X_LDO4_V_OUT_3000mV_END   \
+       (AXP20X_LDO4_V_OUT_3000mV_START + AXP20X_LDO4_V_OUT_3000mV_STEPS)
+#define AXP20X_LDO4_V_OUT_NUM_VOLTAGES 16
+
 #define AXP22X_IO_ENABLED              0x03
 #define AXP22X_IO_DISABLED             0x04
 
-#define AXP20X_WORKMODE_DCDC2_MASK     BIT(2)
-#define AXP20X_WORKMODE_DCDC3_MASK     BIT(1)
-#define AXP22X_WORKMODE_DCDCX_MASK(x)  BIT(x)
-
-#define AXP20X_FREQ_DCDC_MASK          0x0f
+#define AXP22X_WORKMODE_DCDCX_MASK(x)  BIT_MASK(x)
 
 #define AXP22X_MISC_N_VBUSEN_FUNC      BIT(4)
 
+#define AXP22X_DCDC1_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DCDC2_V_OUT_MASK                GENMASK(5, 0)
+#define AXP22X_DCDC3_V_OUT_MASK                GENMASK(5, 0)
+#define AXP22X_DCDC4_V_OUT_MASK                GENMASK(5, 0)
+#define AXP22X_DCDC5_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DC5LDO_V_OUT_MASK       GENMASK(2, 0)
+#define AXP22X_ALDO1_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_ALDO2_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_ALDO3_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DLDO1_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DLDO2_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DLDO3_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_DLDO4_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_ELDO1_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_ELDO2_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_ELDO3_V_OUT_MASK                GENMASK(4, 0)
+#define AXP22X_LDO_IO0_V_OUT_MASK      GENMASK(4, 0)
+#define AXP22X_LDO_IO1_V_OUT_MASK      GENMASK(4, 0)
+
+#define AXP22X_PWR_OUT_DC5LDO_MASK     BIT_MASK(0)
+#define AXP22X_PWR_OUT_DCDC1_MASK      BIT_MASK(1)
+#define AXP22X_PWR_OUT_DCDC2_MASK      BIT_MASK(2)
+#define AXP22X_PWR_OUT_DCDC3_MASK      BIT_MASK(3)
+#define AXP22X_PWR_OUT_DCDC4_MASK      BIT_MASK(4)
+#define AXP22X_PWR_OUT_DCDC5_MASK      BIT_MASK(5)
+#define AXP22X_PWR_OUT_ALDO1_MASK      BIT_MASK(6)
+#define AXP22X_PWR_OUT_ALDO2_MASK      BIT_MASK(7)
+
+#define AXP22X_PWR_OUT_SW_MASK         BIT_MASK(6)
+#define AXP22X_PWR_OUT_DC1SW_MASK      BIT_MASK(7)
+
+#define AXP22X_PWR_OUT_ELDO1_MASK      BIT_MASK(0)
+#define AXP22X_PWR_OUT_ELDO2_MASK      BIT_MASK(1)
+#define AXP22X_PWR_OUT_ELDO3_MASK      BIT_MASK(2)
+#define AXP22X_PWR_OUT_DLDO1_MASK      BIT_MASK(3)
+#define AXP22X_PWR_OUT_DLDO2_MASK      BIT_MASK(4)
+#define AXP22X_PWR_OUT_DLDO3_MASK      BIT_MASK(5)
+#define AXP22X_PWR_OUT_DLDO4_MASK      BIT_MASK(6)
+#define AXP22X_PWR_OUT_ALDO3_MASK      BIT_MASK(7)
+
+#define AXP803_PWR_OUT_DCDC1_MASK      BIT_MASK(0)
+#define AXP803_PWR_OUT_DCDC2_MASK      BIT_MASK(1)
+#define AXP803_PWR_OUT_DCDC3_MASK      BIT_MASK(2)
+#define AXP803_PWR_OUT_DCDC4_MASK      BIT_MASK(3)
+#define AXP803_PWR_OUT_DCDC5_MASK      BIT_MASK(4)
+#define AXP803_PWR_OUT_DCDC6_MASK      BIT_MASK(5)
+
+#define AXP803_PWR_OUT_FLDO1_MASK      BIT_MASK(2)
+#define AXP803_PWR_OUT_FLDO2_MASK      BIT_MASK(3)
+
+#define AXP803_DCDC1_V_OUT_MASK                GENMASK(4, 0)
+#define AXP803_DCDC2_V_OUT_MASK                GENMASK(6, 0)
+#define AXP803_DCDC3_V_OUT_MASK                GENMASK(6, 0)
+#define AXP803_DCDC4_V_OUT_MASK                GENMASK(6, 0)
+#define AXP803_DCDC5_V_OUT_MASK                GENMASK(6, 0)
+#define AXP803_DCDC6_V_OUT_MASK                GENMASK(6, 0)
+
+#define AXP803_FLDO1_V_OUT_MASK                GENMASK(3, 0)
+#define AXP803_FLDO2_V_OUT_MASK                GENMASK(3, 0)
+
+#define AXP803_DCDC23_POLYPHASE_DUAL   BIT(6)
+#define AXP803_DCDC56_POLYPHASE_DUAL   BIT(5)
+
+#define AXP803_DCDC234_500mV_START     0x00
+#define AXP803_DCDC234_500mV_STEPS     70
+#define AXP803_DCDC234_500mV_END       \
+       (AXP803_DCDC234_500mV_START + AXP803_DCDC234_500mV_STEPS)
+#define AXP803_DCDC234_1220mV_START    0x47
+#define AXP803_DCDC234_1220mV_STEPS    4
+#define AXP803_DCDC234_1220mV_END      \
+       (AXP803_DCDC234_1220mV_START + AXP803_DCDC234_1220mV_STEPS)
+#define AXP803_DCDC234_NUM_VOLTAGES    76
+
+#define AXP803_DCDC5_800mV_START       0x00
+#define AXP803_DCDC5_800mV_STEPS       32
+#define AXP803_DCDC5_800mV_END         \
+       (AXP803_DCDC5_800mV_START + AXP803_DCDC5_800mV_STEPS)
+#define AXP803_DCDC5_1140mV_START      0x21
+#define AXP803_DCDC5_1140mV_STEPS      35
+#define AXP803_DCDC5_1140mV_END                \
+       (AXP803_DCDC5_1140mV_START + AXP803_DCDC5_1140mV_STEPS)
+#define AXP803_DCDC5_NUM_VOLTAGES      68
+
+#define AXP803_DCDC6_600mV_START       0x00
+#define AXP803_DCDC6_600mV_STEPS       50
+#define AXP803_DCDC6_600mV_END         \
+       (AXP803_DCDC6_600mV_START + AXP803_DCDC6_600mV_STEPS)
+#define AXP803_DCDC6_1120mV_START      0x33
+#define AXP803_DCDC6_1120mV_STEPS      14
+#define AXP803_DCDC6_1120mV_END                \
+       (AXP803_DCDC6_1120mV_START + AXP803_DCDC6_1120mV_STEPS)
+#define AXP803_DCDC6_NUM_VOLTAGES      72
+
+#define AXP803_DLDO2_700mV_START       0x00
+#define AXP803_DLDO2_700mV_STEPS       26
+#define AXP803_DLDO2_700mV_END         \
+       (AXP803_DLDO2_700mV_START + AXP803_DLDO2_700mV_STEPS)
+#define AXP803_DLDO2_3400mV_START      0x1b
+#define AXP803_DLDO2_3400mV_STEPS      4
+#define AXP803_DLDO2_3400mV_END                \
+       (AXP803_DLDO2_3400mV_START + AXP803_DLDO2_3400mV_STEPS)
+#define AXP803_DLDO2_NUM_VOLTAGES      32
+
+#define AXP806_DCDCA_V_CTRL_MASK       GENMASK(6, 0)
+#define AXP806_DCDCB_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_DCDCC_V_CTRL_MASK       GENMASK(6, 0)
+#define AXP806_DCDCD_V_CTRL_MASK       GENMASK(5, 0)
+#define AXP806_DCDCE_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_ALDO1_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_ALDO2_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_ALDO3_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_BLDO1_V_CTRL_MASK       GENMASK(3, 0)
+#define AXP806_BLDO2_V_CTRL_MASK       GENMASK(3, 0)
+#define AXP806_BLDO3_V_CTRL_MASK       GENMASK(3, 0)
+#define AXP806_BLDO4_V_CTRL_MASK       GENMASK(3, 0)
+#define AXP806_CLDO1_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_CLDO2_V_CTRL_MASK       GENMASK(4, 0)
+#define AXP806_CLDO3_V_CTRL_MASK       GENMASK(4, 0)
+
+#define AXP806_PWR_OUT_DCDCA_MASK      BIT_MASK(0)
+#define AXP806_PWR_OUT_DCDCB_MASK      BIT_MASK(1)
+#define AXP806_PWR_OUT_DCDCC_MASK      BIT_MASK(2)
+#define AXP806_PWR_OUT_DCDCD_MASK      BIT_MASK(3)
+#define AXP806_PWR_OUT_DCDCE_MASK      BIT_MASK(4)
+#define AXP806_PWR_OUT_ALDO1_MASK      BIT_MASK(5)
+#define AXP806_PWR_OUT_ALDO2_MASK      BIT_MASK(6)
+#define AXP806_PWR_OUT_ALDO3_MASK      BIT_MASK(7)
+#define AXP806_PWR_OUT_BLDO1_MASK      BIT_MASK(0)
+#define AXP806_PWR_OUT_BLDO2_MASK      BIT_MASK(1)
+#define AXP806_PWR_OUT_BLDO3_MASK      BIT_MASK(2)
+#define AXP806_PWR_OUT_BLDO4_MASK      BIT_MASK(3)
+#define AXP806_PWR_OUT_CLDO1_MASK      BIT_MASK(4)
+#define AXP806_PWR_OUT_CLDO2_MASK      BIT_MASK(5)
+#define AXP806_PWR_OUT_CLDO3_MASK      BIT_MASK(6)
+#define AXP806_PWR_OUT_SW_MASK         BIT_MASK(7)
+
+#define AXP806_DCDCAB_POLYPHASE_DUAL   0x40
+#define AXP806_DCDCABC_POLYPHASE_TRI   0x80
+#define AXP806_DCDCABC_POLYPHASE_MASK  GENMASK(7, 6)
+
+#define AXP806_DCDCDE_POLYPHASE_DUAL   BIT(5)
+
+#define AXP806_DCDCA_600mV_START       0x00
+#define AXP806_DCDCA_600mV_STEPS       50
+#define AXP806_DCDCA_600mV_END         \
+       (AXP806_DCDCA_600mV_START + AXP806_DCDCA_600mV_STEPS)
+#define AXP806_DCDCA_1120mV_START      0x33
+#define AXP806_DCDCA_1120mV_STEPS      14
+#define AXP806_DCDCA_1120mV_END                \
+       (AXP806_DCDCA_1120mV_START + AXP806_DCDCA_1120mV_STEPS)
+#define AXP806_DCDCA_NUM_VOLTAGES      72
+
+#define AXP806_DCDCD_600mV_START       0x00
+#define AXP806_DCDCD_600mV_STEPS       45
+#define AXP806_DCDCD_600mV_END         \
+       (AXP806_DCDCD_600mV_START + AXP806_DCDCD_600mV_STEPS)
+#define AXP806_DCDCD_1600mV_START      0x2e
+#define AXP806_DCDCD_1600mV_STEPS      17
+#define AXP806_DCDCD_1600mV_END                \
+       (AXP806_DCDCD_1600mV_START + AXP806_DCDCD_1600mV_STEPS)
+#define AXP806_DCDCD_NUM_VOLTAGES      64
+
+#define AXP809_DCDC4_600mV_START       0x00
+#define AXP809_DCDC4_600mV_STEPS       47
+#define AXP809_DCDC4_600mV_END         \
+       (AXP809_DCDC4_600mV_START + AXP809_DCDC4_600mV_STEPS)
+#define AXP809_DCDC4_1800mV_START      0x30
+#define AXP809_DCDC4_1800mV_STEPS      8
+#define AXP809_DCDC4_1800mV_END                \
+       (AXP809_DCDC4_1800mV_START + AXP809_DCDC4_1800mV_STEPS)
+#define AXP809_DCDC4_NUM_VOLTAGES      57
+
+#define AXP813_DCDC7_V_OUT_MASK                GENMASK(6, 0)
+
+#define AXP813_PWR_OUT_DCDC7_MASK      BIT_MASK(6)
+
 #define AXP_DESC_IO(_family, _id, _match, _supply, _min, _max, _step, _vreg,   \
                    _vmask, _ereg, _emask, _enable_val, _disable_val)           \
        [_family##_##_id] = {                                                   \
                .ops            = &axp20x_ops_range,                            \
        }
 
+static const int axp209_dcdc2_ldo3_slew_rates[] = {
+       1600,
+        800,
+};
+
+static int axp20x_set_ramp_delay(struct regulator_dev *rdev, int ramp)
+{
+       struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+       const struct regulator_desc *desc = rdev->desc;
+       u8 reg, mask, enable, cfg = 0xff;
+       const int *slew_rates;
+       int rate_count = 0;
+
+       if (!rdev)
+               return -EINVAL;
+
+       switch (axp20x->variant) {
+       case AXP209_ID:
+               if (desc->id == AXP20X_DCDC2) {
+                       slew_rates = axp209_dcdc2_ldo3_slew_rates;
+                       rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates);
+                       reg = AXP20X_DCDC2_LDO3_V_RAMP;
+                       mask = AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_RATE_MASK |
+                              AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN_MASK;
+                       enable = (ramp > 0) ?
+                                AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN :
+                                !AXP20X_DCDC2_LDO3_V_RAMP_DCDC2_EN;
+                       break;
+               }
+
+               if (desc->id == AXP20X_LDO3) {
+                       slew_rates = axp209_dcdc2_ldo3_slew_rates;
+                       rate_count = ARRAY_SIZE(axp209_dcdc2_ldo3_slew_rates);
+                       reg = AXP20X_DCDC2_LDO3_V_RAMP;
+                       mask = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE_MASK |
+                              AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN_MASK;
+                       enable = (ramp > 0) ?
+                                AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN :
+                                !AXP20X_DCDC2_LDO3_V_RAMP_LDO3_EN;
+                       break;
+               }
+
+               if (rate_count > 0)
+                       break;
+
+               /* fall through */
+       default:
+               /* Not supported for this regulator */
+               return -ENOTSUPP;
+       }
+
+       if (ramp == 0) {
+               cfg = enable;
+       } else {
+               int i;
+
+               for (i = 0; i < rate_count; i++) {
+                       if (ramp <= slew_rates[i])
+                               cfg = AXP20X_DCDC2_LDO3_V_RAMP_LDO3_RATE(i);
+                       else
+                               break;
+               }
+
+               if (cfg == 0xff) {
+                       dev_err(axp20x->dev, "unsupported ramp value %d", ramp);
+                       return -EINVAL;
+               }
+
+               cfg |= enable;
+       }
+
+       return regmap_update_bits(axp20x->regmap, reg, mask, cfg);
+}
+
+static int axp20x_regulator_enable_regmap(struct regulator_dev *rdev)
+{
+       struct axp20x_dev *axp20x = rdev_get_drvdata(rdev);
+       const struct regulator_desc *desc = rdev->desc;
+
+       if (!rdev)
+               return -EINVAL;
+
+       switch (axp20x->variant) {
+       case AXP209_ID:
+               if ((desc->id == AXP20X_LDO3) &&
+                   rdev->constraints && rdev->constraints->soft_start) {
+                       int v_out;
+                       int ret;
+
+                       /*
+                        * On some boards, the LDO3 can be overloaded when
+                        * turning on, causing the entire PMIC to shutdown
+                        * without warning. Turning it on at the minimal voltage
+                        * and then setting the voltage to the requested value
+                        * works reliably.
+                        */
+                       if (regulator_is_enabled_regmap(rdev))
+                               break;
+
+                       v_out = regulator_get_voltage_sel_regmap(rdev);
+                       if (v_out < 0)
+                               return v_out;
+
+                       if (v_out == 0)
+                               break;
+
+                       ret = regulator_set_voltage_sel_regmap(rdev, 0x00);
+                       /*
+                        * A small pause is needed between
+                        * setting the voltage and enabling the LDO to give the
+                        * internal state machine time to process the request.
+                        */
+                       usleep_range(1000, 5000);
+                       ret |= regulator_enable_regmap(rdev);
+                       ret |= regulator_set_voltage_sel_regmap(rdev, v_out);
+
+                       return ret;
+               }
+               break;
+       default:
+               /* No quirks */
+               break;
+       }
+
+       return regulator_enable_regmap(rdev);
+};
+
 static const struct regulator_ops axp20x_ops_fixed = {
        .list_voltage           = regulator_list_voltage_linear,
 };
@@ -145,9 +503,10 @@ static const struct regulator_ops axp20x_ops = {
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
        .get_voltage_sel        = regulator_get_voltage_sel_regmap,
        .list_voltage           = regulator_list_voltage_linear,
-       .enable                 = regulator_enable_regmap,
+       .enable                 = axp20x_regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .is_enabled             = regulator_is_enabled_regmap,
+       .set_ramp_delay         = axp20x_set_ramp_delay,
 };
 
 static const struct regulator_ops axp20x_ops_sw = {
@@ -157,77 +516,116 @@ static const struct regulator_ops axp20x_ops_sw = {
 };
 
 static const struct regulator_linear_range axp20x_ldo4_ranges[] = {
-       REGULATOR_LINEAR_RANGE(1250000, 0x0, 0x0, 0),
-       REGULATOR_LINEAR_RANGE(1300000, 0x1, 0x8, 100000),
-       REGULATOR_LINEAR_RANGE(2500000, 0x9, 0x9, 0),
-       REGULATOR_LINEAR_RANGE(2700000, 0xa, 0xb, 100000),
-       REGULATOR_LINEAR_RANGE(3000000, 0xc, 0xf, 100000),
+       REGULATOR_LINEAR_RANGE(1250000,
+                              AXP20X_LDO4_V_OUT_1250mV_START,
+                              AXP20X_LDO4_V_OUT_1250mV_END,
+                              0),
+       REGULATOR_LINEAR_RANGE(1300000,
+                              AXP20X_LDO4_V_OUT_1300mV_START,
+                              AXP20X_LDO4_V_OUT_1300mV_END,
+                              100000),
+       REGULATOR_LINEAR_RANGE(2500000,
+                              AXP20X_LDO4_V_OUT_2500mV_START,
+                              AXP20X_LDO4_V_OUT_2500mV_END,
+                              0),
+       REGULATOR_LINEAR_RANGE(2700000,
+                              AXP20X_LDO4_V_OUT_2700mV_START,
+                              AXP20X_LDO4_V_OUT_2700mV_END,
+                              100000),
+       REGULATOR_LINEAR_RANGE(3000000,
+                              AXP20X_LDO4_V_OUT_3000mV_START,
+                              AXP20X_LDO4_V_OUT_3000mV_END,
+                              100000),
 };
 
 static const struct regulator_desc axp20x_regulators[] = {
        AXP_DESC(AXP20X, DCDC2, "dcdc2", "vin2", 700, 2275, 25,
-                AXP20X_DCDC2_V_OUT, 0x3f, AXP20X_PWR_OUT_CTRL, 0x10),
+                AXP20X_DCDC2_V_OUT, AXP20X_DCDC2_V_OUT_MASK,
+                AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC2_MASK),
        AXP_DESC(AXP20X, DCDC3, "dcdc3", "vin3", 700, 3500, 25,
-                AXP20X_DCDC3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x02),
+                AXP20X_DCDC3_V_OUT, AXP20X_DCDC3_V_OUT_MASK,
+                AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_DCDC3_MASK),
        AXP_DESC_FIXED(AXP20X, LDO1, "ldo1", "acin", 1300),
        AXP_DESC(AXP20X, LDO2, "ldo2", "ldo24in", 1800, 3300, 100,
-                AXP20X_LDO24_V_OUT, 0xf0, AXP20X_PWR_OUT_CTRL, 0x04),
+                AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK,
+                AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO2_MASK),
        AXP_DESC(AXP20X, LDO3, "ldo3", "ldo3in", 700, 3500, 25,
-                AXP20X_LDO3_V_OUT, 0x7f, AXP20X_PWR_OUT_CTRL, 0x40),
-       AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in", axp20x_ldo4_ranges,
-                       16, AXP20X_LDO24_V_OUT, 0x0f, AXP20X_PWR_OUT_CTRL,
-                       0x08),
+                AXP20X_LDO3_V_OUT, AXP20X_LDO3_V_OUT_MASK,
+                AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO3_MASK),
+       AXP_DESC_RANGES(AXP20X, LDO4, "ldo4", "ldo24in",
+                       axp20x_ldo4_ranges, AXP20X_LDO4_V_OUT_NUM_VOLTAGES,
+                       AXP20X_LDO24_V_OUT, AXP20X_LDO24_V_OUT_MASK,
+                       AXP20X_PWR_OUT_CTRL, AXP20X_PWR_OUT_LDO4_MASK),
        AXP_DESC_IO(AXP20X, LDO5, "ldo5", "ldo5in", 1800, 3300, 100,
-                   AXP20X_LDO5_V_OUT, 0xf0, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP20X_LDO5_V_OUT, AXP20X_LDO5_V_OUT_MASK,
+                   AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK,
                    AXP20X_IO_ENABLED, AXP20X_IO_DISABLED),
 };
 
 static const struct regulator_desc axp22x_regulators[] = {
        AXP_DESC(AXP22X, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
-                AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+                AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK),
        AXP_DESC(AXP22X, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
-                AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+                AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK),
        AXP_DESC(AXP22X, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
-                AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
+                AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK),
        AXP_DESC(AXP22X, DCDC4, "dcdc4", "vin4", 600, 1540, 20,
-                AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(4)),
+                AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK),
        AXP_DESC(AXP22X, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
-                AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)),
+                AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK),
        /* secondary switchable output of DCDC1 */
-       AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
-                   BIT(7)),
+       AXP_DESC_SW(AXP22X, DC1SW, "dc1sw", NULL,
+                   AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK),
        /* LDO regulator internally chained to DCDC5 */
        AXP_DESC(AXP22X, DC5LDO, "dc5ldo", NULL, 700, 1400, 100,
-                AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+                AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK),
        AXP_DESC(AXP22X, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+                AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK),
        AXP_DESC(AXP22X, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+                AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK),
        AXP_DESC(AXP22X, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+                AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP22X_PWR_OUT_ALDO3_MASK),
        AXP_DESC(AXP22X, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
+                AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
        AXP_DESC(AXP22X, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+                AXP22X_DLDO2_V_OUT, AXP22X_PWR_OUT_DLDO2_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
        AXP_DESC(AXP22X, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+                AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK),
        AXP_DESC(AXP22X, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+                AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK),
        AXP_DESC(AXP22X, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+                AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK),
        AXP_DESC(AXP22X, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+                AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK),
        AXP_DESC(AXP22X, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+                AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK),
        /* Note the datasheet only guarantees reliable operation up to
         * 3.3V, this needs to be enforced via dts provided constraints */
        AXP_DESC_IO(AXP22X, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100,
-                   AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK,
+                   AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        /* Note the datasheet only guarantees reliable operation up to
         * 3.3V, this needs to be enforced via dts provided constraints */
        AXP_DESC_IO(AXP22X, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100,
-                   AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+                   AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK,
+                   AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        AXP_DESC_FIXED(AXP22X, RTC_LDO, "rtc_ldo", "ips", 3000),
 };
@@ -240,240 +638,354 @@ static const struct regulator_desc axp22x_drivevbus_regulator = {
        .type           = REGULATOR_VOLTAGE,
        .owner          = THIS_MODULE,
        .enable_reg     = AXP20X_VBUS_IPSOUT_MGMT,
-       .enable_mask    = BIT(2),
+       .enable_mask    = AXP20X_VBUS_IPSOUT_MGMT_MASK,
        .ops            = &axp20x_ops_sw,
 };
 
 /* DCDC ranges shared with AXP813 */
 static const struct regulator_linear_range axp803_dcdc234_ranges[] = {
-       REGULATOR_LINEAR_RANGE(500000, 0x0, 0x46, 10000),
-       REGULATOR_LINEAR_RANGE(1220000, 0x47, 0x4b, 20000),
+       REGULATOR_LINEAR_RANGE(500000,
+                              AXP803_DCDC234_500mV_START,
+                              AXP803_DCDC234_500mV_END,
+                              10000),
+       REGULATOR_LINEAR_RANGE(1220000,
+                              AXP803_DCDC234_1220mV_START,
+                              AXP803_DCDC234_1220mV_END,
+                              20000),
 };
 
 static const struct regulator_linear_range axp803_dcdc5_ranges[] = {
-       REGULATOR_LINEAR_RANGE(800000, 0x0, 0x20, 10000),
-       REGULATOR_LINEAR_RANGE(1140000, 0x21, 0x44, 20000),
+       REGULATOR_LINEAR_RANGE(800000,
+                              AXP803_DCDC5_800mV_START,
+                              AXP803_DCDC5_800mV_END,
+                              10000),
+       REGULATOR_LINEAR_RANGE(1140000,
+                              AXP803_DCDC5_1140mV_START,
+                              AXP803_DCDC5_1140mV_END,
+                              20000),
 };
 
 static const struct regulator_linear_range axp803_dcdc6_ranges[] = {
-       REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000),
-       REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000),
+       REGULATOR_LINEAR_RANGE(600000,
+                              AXP803_DCDC6_600mV_START,
+                              AXP803_DCDC6_600mV_END,
+                              10000),
+       REGULATOR_LINEAR_RANGE(1120000,
+                              AXP803_DCDC6_1120mV_START,
+                              AXP803_DCDC6_1120mV_END,
+                              20000),
 };
 
-/* AXP806's CLDO2 and AXP809's DLDO1 shares the same range */
+/* AXP806's CLDO2 and AXP809's DLDO1 share the same range */
 static const struct regulator_linear_range axp803_dldo2_ranges[] = {
-       REGULATOR_LINEAR_RANGE(700000, 0x0, 0x1a, 100000),
-       REGULATOR_LINEAR_RANGE(3400000, 0x1b, 0x1f, 200000),
+       REGULATOR_LINEAR_RANGE(700000,
+                              AXP803_DLDO2_700mV_START,
+                              AXP803_DLDO2_700mV_END,
+                              100000),
+       REGULATOR_LINEAR_RANGE(3400000,
+                              AXP803_DLDO2_3400mV_START,
+                              AXP803_DLDO2_3400mV_END,
+                              200000),
 };
 
 static const struct regulator_desc axp803_regulators[] = {
        AXP_DESC(AXP803, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
-                AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)),
-       AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(1)),
-       AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(2)),
-       AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(3)),
-       AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges,
-                       68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(4)),
-       AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges,
-                       72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(5)),
+                AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK),
+       AXP_DESC_RANGES(AXP803, DCDC2, "dcdc2", "vin2",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK),
+       AXP_DESC_RANGES(AXP803, DCDC3, "dcdc3", "vin3",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK),
+       AXP_DESC_RANGES(AXP803, DCDC4, "dcdc4", "vin4",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK),
+       AXP_DESC_RANGES(AXP803, DCDC5, "dcdc5", "vin5",
+                       axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES,
+                       AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK),
+       AXP_DESC_RANGES(AXP803, DCDC6, "dcdc6", "vin6",
+                       axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES,
+                       AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK),
        /* secondary switchable output of DCDC1 */
-       AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
-                   BIT(7)),
+       AXP_DESC_SW(AXP803, DC1SW, "dc1sw", NULL,
+                   AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK),
        AXP_DESC(AXP803, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)),
+                AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK),
        AXP_DESC(AXP803, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)),
+                AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK),
        AXP_DESC(AXP803, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+                AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK),
        AXP_DESC(AXP803, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
-       AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges,
-                       32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
-                       BIT(4)),
+                AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
+       AXP_DESC_RANGES(AXP803, DLDO2, "dldo2", "dldoin",
+                       axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES,
+                       AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT,
+                       AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
        AXP_DESC(AXP803, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+                AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK),
        AXP_DESC(AXP803, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+                AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK),
        AXP_DESC(AXP803, ELDO1, "eldo1", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+                AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK),
        AXP_DESC(AXP803, ELDO2, "eldo2", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+                AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK),
        AXP_DESC(AXP803, ELDO3, "eldo3", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+                AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK),
        AXP_DESC(AXP803, FLDO1, "fldo1", "fldoin", 700, 1450, 50,
-                AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)),
+                AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK),
        AXP_DESC(AXP803, FLDO2, "fldo2", "fldoin", 700, 1450, 50,
-                AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)),
+                AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK),
        AXP_DESC_IO(AXP803, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100,
-                   AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK,
+                   AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        AXP_DESC_IO(AXP803, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100,
-                   AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+                   AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK,
+                   AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        AXP_DESC_FIXED(AXP803, RTC_LDO, "rtc-ldo", "ips", 3000),
 };
 
 static const struct regulator_linear_range axp806_dcdca_ranges[] = {
-       REGULATOR_LINEAR_RANGE(600000, 0x0, 0x32, 10000),
-       REGULATOR_LINEAR_RANGE(1120000, 0x33, 0x47, 20000),
+       REGULATOR_LINEAR_RANGE(600000,
+                              AXP806_DCDCA_600mV_START,
+                              AXP806_DCDCA_600mV_END,
+                              10000),
+       REGULATOR_LINEAR_RANGE(1120000,
+                              AXP806_DCDCA_1120mV_START,
+                              AXP806_DCDCA_1120mV_END,
+                              20000),
 };
 
 static const struct regulator_linear_range axp806_dcdcd_ranges[] = {
-       REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2d, 20000),
-       REGULATOR_LINEAR_RANGE(1600000, 0x2e, 0x3f, 100000),
+       REGULATOR_LINEAR_RANGE(600000,
+                              AXP806_DCDCD_600mV_START,
+                              AXP806_DCDCD_600mV_END,
+                              20000),
+       REGULATOR_LINEAR_RANGE(1600000,
+                              AXP806_DCDCD_600mV_START,
+                              AXP806_DCDCD_600mV_END,
+                              100000),
 };
 
 static const struct regulator_desc axp806_regulators[] = {
-       AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina", axp806_dcdca_ranges,
-                       72, AXP806_DCDCA_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
-                       BIT(0)),
+       AXP_DESC_RANGES(AXP806, DCDCA, "dcdca", "vina",
+                       axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES,
+                       AXP806_DCDCA_V_CTRL, AXP806_DCDCA_V_CTRL_MASK,
+                       AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCA_MASK),
        AXP_DESC(AXP806, DCDCB, "dcdcb", "vinb", 1000, 2550, 50,
-                AXP806_DCDCB_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(1)),
-       AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc", axp806_dcdca_ranges,
-                       72, AXP806_DCDCC_V_CTRL, 0x7f, AXP806_PWR_OUT_CTRL1,
-                       BIT(2)),
-       AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind", axp806_dcdcd_ranges,
-                       64, AXP806_DCDCD_V_CTRL, 0x3f, AXP806_PWR_OUT_CTRL1,
-                       BIT(3)),
+                AXP806_DCDCB_V_CTRL, AXP806_DCDCB_V_CTRL,
+                AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCB_MASK),
+       AXP_DESC_RANGES(AXP806, DCDCC, "dcdcc", "vinc",
+                       axp806_dcdca_ranges, AXP806_DCDCA_NUM_VOLTAGES,
+                       AXP806_DCDCC_V_CTRL, AXP806_DCDCC_V_CTRL_MASK,
+                       AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCC_MASK),
+       AXP_DESC_RANGES(AXP806, DCDCD, "dcdcd", "vind",
+                       axp806_dcdcd_ranges, AXP806_DCDCD_NUM_VOLTAGES,
+                       AXP806_DCDCD_V_CTRL, AXP806_DCDCD_V_CTRL_MASK,
+                       AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCD_MASK),
        AXP_DESC(AXP806, DCDCE, "dcdce", "vine", 1100, 3400, 100,
-                AXP806_DCDCE_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(4)),
+                AXP806_DCDCE_V_CTRL, AXP806_DCDCE_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_DCDCE_MASK),
        AXP_DESC(AXP806, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
-                AXP806_ALDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(5)),
+                AXP806_ALDO1_V_CTRL, AXP806_ALDO1_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO1_MASK),
        AXP_DESC(AXP806, ALDO2, "aldo2", "aldoin", 700, 3400, 100,
-                AXP806_ALDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(6)),
+                AXP806_ALDO2_V_CTRL, AXP806_ALDO2_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO2_MASK),
        AXP_DESC(AXP806, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
-                AXP806_ALDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL1, BIT(7)),
+                AXP806_ALDO3_V_CTRL, AXP806_ALDO3_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL1, AXP806_PWR_OUT_ALDO3_MASK),
        AXP_DESC(AXP806, BLDO1, "bldo1", "bldoin", 700, 1900, 100,
-                AXP806_BLDO1_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(0)),
+                AXP806_BLDO1_V_CTRL, AXP806_BLDO1_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO1_MASK),
        AXP_DESC(AXP806, BLDO2, "bldo2", "bldoin", 700, 1900, 100,
-                AXP806_BLDO2_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(1)),
+                AXP806_BLDO2_V_CTRL, AXP806_BLDO2_V_CTRL,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO2_MASK),
        AXP_DESC(AXP806, BLDO3, "bldo3", "bldoin", 700, 1900, 100,
-                AXP806_BLDO3_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(2)),
+                AXP806_BLDO3_V_CTRL, AXP806_BLDO3_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO3_MASK),
        AXP_DESC(AXP806, BLDO4, "bldo4", "bldoin", 700, 1900, 100,
-                AXP806_BLDO4_V_CTRL, 0x0f, AXP806_PWR_OUT_CTRL2, BIT(3)),
+                AXP806_BLDO4_V_CTRL, AXP806_BLDO4_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_BLDO4_MASK),
        AXP_DESC(AXP806, CLDO1, "cldo1", "cldoin", 700, 3300, 100,
-                AXP806_CLDO1_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(4)),
-       AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin", axp803_dldo2_ranges,
-                       32, AXP806_CLDO2_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2,
-                       BIT(5)),
+                AXP806_CLDO1_V_CTRL, AXP806_CLDO1_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO1_MASK),
+       AXP_DESC_RANGES(AXP806, CLDO2, "cldo2", "cldoin",
+                       axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES,
+                       AXP806_CLDO2_V_CTRL, AXP806_CLDO2_V_CTRL_MASK,
+                       AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO2_MASK),
        AXP_DESC(AXP806, CLDO3, "cldo3", "cldoin", 700, 3300, 100,
-                AXP806_CLDO3_V_CTRL, 0x1f, AXP806_PWR_OUT_CTRL2, BIT(6)),
-       AXP_DESC_SW(AXP806, SW, "sw", "swin", AXP806_PWR_OUT_CTRL2, BIT(7)),
+                AXP806_CLDO3_V_CTRL, AXP806_CLDO3_V_CTRL_MASK,
+                AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_CLDO3_MASK),
+       AXP_DESC_SW(AXP806, SW, "sw", "swin",
+                   AXP806_PWR_OUT_CTRL2, AXP806_PWR_OUT_SW_MASK),
 };
 
 static const struct regulator_linear_range axp809_dcdc4_ranges[] = {
-       REGULATOR_LINEAR_RANGE(600000, 0x0, 0x2f, 20000),
-       REGULATOR_LINEAR_RANGE(1800000, 0x30, 0x38, 100000),
+       REGULATOR_LINEAR_RANGE(600000,
+                              AXP809_DCDC4_600mV_START,
+                              AXP809_DCDC4_600mV_END,
+                              20000),
+       REGULATOR_LINEAR_RANGE(1800000,
+                              AXP809_DCDC4_1800mV_START,
+                              AXP809_DCDC4_1800mV_END,
+                              100000),
 };
 
 static const struct regulator_desc axp809_regulators[] = {
        AXP_DESC(AXP809, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
-                AXP22X_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(1)),
+                AXP22X_DCDC1_V_OUT, AXP22X_DCDC1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC1_MASK),
        AXP_DESC(AXP809, DCDC2, "dcdc2", "vin2", 600, 1540, 20,
-                AXP22X_DCDC2_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(2)),
+                AXP22X_DCDC2_V_OUT, AXP22X_DCDC2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC2_MASK),
        AXP_DESC(AXP809, DCDC3, "dcdc3", "vin3", 600, 1860, 20,
-                AXP22X_DCDC3_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1, BIT(3)),
-       AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4", axp809_dcdc4_ranges,
-                       57, AXP22X_DCDC4_V_OUT, 0x3f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(4)),
+                AXP22X_DCDC3_V_OUT, AXP22X_DCDC3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC3_MASK),
+       AXP_DESC_RANGES(AXP809, DCDC4, "dcdc4", "vin4",
+                       axp809_dcdc4_ranges, AXP809_DCDC4_NUM_VOLTAGES,
+                       AXP22X_DCDC4_V_OUT, AXP22X_DCDC4_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC4_MASK),
        AXP_DESC(AXP809, DCDC5, "dcdc5", "vin5", 1000, 2550, 50,
-                AXP22X_DCDC5_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(5)),
+                AXP22X_DCDC5_V_OUT, AXP22X_DCDC5_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DCDC5_MASK),
        /* secondary switchable output of DCDC1 */
-       AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL, AXP22X_PWR_OUT_CTRL2,
-                   BIT(7)),
+       AXP_DESC_SW(AXP809, DC1SW, "dc1sw", NULL,
+                   AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK),
        /* LDO regulator internally chained to DCDC5 */
        AXP_DESC(AXP809, DC5LDO, "dc5ldo", NULL, 700, 1400, 100,
-                AXP22X_DC5LDO_V_OUT, 0x7, AXP22X_PWR_OUT_CTRL1, BIT(0)),
+                AXP22X_DC5LDO_V_OUT, AXP22X_DC5LDO_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_DC5LDO_MASK),
        AXP_DESC(AXP809, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(6)),
+                AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO1_MASK),
        AXP_DESC(AXP809, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(7)),
+                AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP22X_PWR_OUT_ALDO2_MASK),
        AXP_DESC(AXP809, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
-       AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin", axp803_dldo2_ranges,
-                       32, AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
-                       BIT(3)),
+                AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ALDO3_MASK),
+       AXP_DESC_RANGES(AXP809, DLDO1, "dldo1", "dldoin",
+                       axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES,
+                       AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
        AXP_DESC(AXP809, DLDO2, "dldo2", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(4)),
+                AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
        AXP_DESC(AXP809, ELDO1, "eldo1", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+                AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK),
        AXP_DESC(AXP809, ELDO2, "eldo2", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+                AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK),
        AXP_DESC(AXP809, ELDO3, "eldo3", "eldoin", 700, 3300, 100,
-                AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+                AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK),
        /*
         * Note the datasheet only guarantees reliable operation up to
         * 3.3V, this needs to be enforced via dts provided constraints
         */
        AXP_DESC_IO(AXP809, LDO_IO0, "ldo_io0", "ips", 700, 3800, 100,
-                   AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK,
+                   AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        /*
         * Note the datasheet only guarantees reliable operation up to
         * 3.3V, this needs to be enforced via dts provided constraints
         */
        AXP_DESC_IO(AXP809, LDO_IO1, "ldo_io1", "ips", 700, 3800, 100,
-                   AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+                   AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK,
+                   AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        AXP_DESC_FIXED(AXP809, RTC_LDO, "rtc_ldo", "ips", 1800),
-       AXP_DESC_SW(AXP809, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(6)),
+       AXP_DESC_SW(AXP809, SW, "sw", "swin",
+                   AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_SW_MASK),
 };
 
 static const struct regulator_desc axp813_regulators[] = {
        AXP_DESC(AXP813, DCDC1, "dcdc1", "vin1", 1600, 3400, 100,
-                AXP803_DCDC1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL1, BIT(0)),
-       AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC2_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(1)),
-       AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC3_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(2)),
-       AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4", axp803_dcdc234_ranges,
-                       76, AXP803_DCDC4_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(3)),
-       AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5", axp803_dcdc5_ranges,
-                       68, AXP803_DCDC5_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(4)),
-       AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6", axp803_dcdc6_ranges,
-                       72, AXP803_DCDC6_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(5)),
-       AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7", axp803_dcdc6_ranges,
-                       72, AXP813_DCDC7_V_OUT, 0x7f, AXP22X_PWR_OUT_CTRL1,
-                       BIT(6)),
+                AXP803_DCDC1_V_OUT, AXP803_DCDC1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC1_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC2, "dcdc2", "vin2",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC2_V_OUT, AXP803_DCDC2_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC2_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC3, "dcdc3", "vin3",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC3_V_OUT, AXP803_DCDC3_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC3_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC4, "dcdc4", "vin4",
+                       axp803_dcdc234_ranges, AXP803_DCDC234_NUM_VOLTAGES,
+                       AXP803_DCDC4_V_OUT, AXP803_DCDC4_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC4_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC5, "dcdc5", "vin5",
+                       axp803_dcdc5_ranges, AXP803_DCDC5_NUM_VOLTAGES,
+                       AXP803_DCDC5_V_OUT, AXP803_DCDC5_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC5_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC6, "dcdc6", "vin6",
+                       axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES,
+                       AXP803_DCDC6_V_OUT, AXP803_DCDC6_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP803_PWR_OUT_DCDC6_MASK),
+       AXP_DESC_RANGES(AXP813, DCDC7, "dcdc7", "vin7",
+                       axp803_dcdc6_ranges, AXP803_DCDC6_NUM_VOLTAGES,
+                       AXP813_DCDC7_V_OUT, AXP813_DCDC7_V_OUT_MASK,
+                       AXP22X_PWR_OUT_CTRL1, AXP813_PWR_OUT_DCDC7_MASK),
        AXP_DESC(AXP813, ALDO1, "aldo1", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(5)),
+                AXP22X_ALDO1_V_OUT, AXP22X_ALDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO1_MASK),
        AXP_DESC(AXP813, ALDO2, "aldo2", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(6)),
+                AXP22X_ALDO2_V_OUT, AXP22X_ALDO2_V_OUT,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO2_MASK),
        AXP_DESC(AXP813, ALDO3, "aldo3", "aldoin", 700, 3300, 100,
-                AXP22X_ALDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL3, BIT(7)),
+                AXP22X_ALDO3_V_OUT, AXP22X_ALDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP806_PWR_OUT_ALDO3_MASK),
        AXP_DESC(AXP813, DLDO1, "dldo1", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(3)),
-       AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin", axp803_dldo2_ranges,
-                       32, AXP22X_DLDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2,
-                       BIT(4)),
+                AXP22X_DLDO1_V_OUT, AXP22X_DLDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO1_MASK),
+       AXP_DESC_RANGES(AXP813, DLDO2, "dldo2", "dldoin",
+                       axp803_dldo2_ranges, AXP803_DLDO2_NUM_VOLTAGES,
+                       AXP22X_DLDO2_V_OUT, AXP22X_DLDO2_V_OUT,
+                       AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO2_MASK),
        AXP_DESC(AXP813, DLDO3, "dldo3", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(5)),
+                AXP22X_DLDO3_V_OUT, AXP22X_DLDO3_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO3_MASK),
        AXP_DESC(AXP813, DLDO4, "dldo4", "dldoin", 700, 3300, 100,
-                AXP22X_DLDO4_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(6)),
+                AXP22X_DLDO4_V_OUT, AXP22X_DLDO4_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DLDO4_MASK),
        AXP_DESC(AXP813, ELDO1, "eldo1", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO1_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(0)),
+                AXP22X_ELDO1_V_OUT, AXP22X_ELDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO1_MASK),
        AXP_DESC(AXP813, ELDO2, "eldo2", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO2_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(1)),
+                AXP22X_ELDO2_V_OUT, AXP22X_ELDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO2_MASK),
        AXP_DESC(AXP813, ELDO3, "eldo3", "eldoin", 700, 1900, 50,
-                AXP22X_ELDO3_V_OUT, 0x1f, AXP22X_PWR_OUT_CTRL2, BIT(2)),
+                AXP22X_ELDO3_V_OUT, AXP22X_ELDO3_V_OUT,
+                AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_ELDO3_MASK),
        /* to do / check ... */
        AXP_DESC(AXP813, FLDO1, "fldo1", "fldoin", 700, 1450, 50,
-                AXP803_FLDO1_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(2)),
+                AXP803_FLDO1_V_OUT, AXP803_FLDO1_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO1_MASK),
        AXP_DESC(AXP813, FLDO2, "fldo2", "fldoin", 700, 1450, 50,
-                AXP803_FLDO2_V_OUT, 0x0f, AXP22X_PWR_OUT_CTRL3, BIT(3)),
+                AXP803_FLDO2_V_OUT, AXP803_FLDO2_V_OUT_MASK,
+                AXP22X_PWR_OUT_CTRL3, AXP803_PWR_OUT_FLDO2_MASK),
        /*
         * TODO: FLDO3 = {DCDC5, FLDOIN} / 2
         *
@@ -482,12 +994,15 @@ static const struct regulator_desc axp813_regulators[] = {
         */
        AXP_DESC_FIXED(AXP813, RTC_LDO, "rtc-ldo", "ips", 1800),
        AXP_DESC_IO(AXP813, LDO_IO0, "ldo-io0", "ips", 700, 3300, 100,
-                   AXP22X_LDO_IO0_V_OUT, 0x1f, AXP20X_GPIO0_CTRL, 0x07,
+                   AXP22X_LDO_IO0_V_OUT, AXP22X_LDO_IO0_V_OUT_MASK,
+                   AXP20X_GPIO0_CTRL, AXP20X_GPIO0_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
        AXP_DESC_IO(AXP813, LDO_IO1, "ldo-io1", "ips", 700, 3300, 100,
-                   AXP22X_LDO_IO1_V_OUT, 0x1f, AXP20X_GPIO1_CTRL, 0x07,
+                   AXP22X_LDO_IO1_V_OUT, AXP22X_LDO_IO1_V_OUT_MASK,
+                   AXP20X_GPIO1_CTRL, AXP20X_GPIO1_FUNC_MASK,
                    AXP22X_IO_ENABLED, AXP22X_IO_DISABLED),
-       AXP_DESC_SW(AXP813, SW, "sw", "swin", AXP22X_PWR_OUT_CTRL2, BIT(7)),
+       AXP_DESC_SW(AXP813, SW, "sw", "swin",
+                   AXP22X_PWR_OUT_CTRL2, AXP22X_PWR_OUT_DC1SW_MASK),
 };
 
 static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq)
@@ -663,9 +1178,9 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id)
 
                switch (id) {
                case AXP803_DCDC3:
-                       return !!(reg & BIT(6));
+                       return !!(reg & AXP803_DCDC23_POLYPHASE_DUAL);
                case AXP803_DCDC6:
-                       return !!(reg & BIT(5));
+                       return !!(reg & AXP803_DCDC56_POLYPHASE_DUAL);
                }
                break;
 
@@ -674,12 +1189,15 @@ static bool axp20x_is_polyphase_slave(struct axp20x_dev *axp20x, int id)
 
                switch (id) {
                case AXP806_DCDCB:
-                       return (((reg & GENMASK(7, 6)) == BIT(6)) ||
-                               ((reg & GENMASK(7, 6)) == BIT(7)));
+                       return (((reg & AXP806_DCDCABC_POLYPHASE_MASK) ==
+                               AXP806_DCDCAB_POLYPHASE_DUAL) ||
+                               ((reg & AXP806_DCDCABC_POLYPHASE_MASK) ==
+                               AXP806_DCDCABC_POLYPHASE_TRI));
                case AXP806_DCDCC:
-                       return ((reg & GENMASK(7, 6)) == BIT(7));
+                       return ((reg & AXP806_DCDCABC_POLYPHASE_MASK) ==
+                               AXP806_DCDCABC_POLYPHASE_TRI);
                case AXP806_DCDCE:
-                       return !!(reg & BIT(5));
+                       return !!(reg & AXP806_DCDCDE_POLYPHASE_DUAL);
                }
                break;
 
index 3a47e03..b8dcdc2 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/kernel.h>
 #include <linux/mfd/rohm-bd718x7.h>
 #include <linux/module.h>
+#include <linux/of.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -130,6 +131,7 @@ static struct regulator_ops bd718xx_buck_regulator_nolinear_ops = {
        .disable = regulator_disable_regmap,
        .is_enabled = regulator_is_enabled_regmap,
        .list_voltage = regulator_list_voltage_table,
+       .map_voltage = regulator_map_voltage_ascend,
        .set_voltage_sel = bd718xx_set_voltage_sel_restricted,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
        .set_voltage_time_sel = regulator_set_voltage_time_sel,
@@ -1007,7 +1009,7 @@ static const struct bd718xx_regulator_data bd71837_regulators[] = {
 };
 
 struct bd718xx_pmic_inits {
-       const struct bd718xx_regulator_data (*r_datas)[];
+       const struct bd718xx_regulator_data *r_datas;
        unsigned int r_amount;
 };
 
@@ -1017,11 +1019,11 @@ static int bd718xx_probe(struct platform_device *pdev)
        struct regulator_config config = { 0 };
        struct bd718xx_pmic_inits pmic_regulators[] = {
                [BD718XX_TYPE_BD71837] = {
-                       .r_datas = &bd71837_regulators,
+                       .r_datas = bd71837_regulators,
                        .r_amount = ARRAY_SIZE(bd71837_regulators),
                },
                [BD718XX_TYPE_BD71847] = {
-                       .r_datas = &bd71847_regulators,
+                       .r_datas = bd71847_regulators,
                        .r_amount = ARRAY_SIZE(bd71847_regulators),
                },
        };
@@ -1053,13 +1055,36 @@ static int bd718xx_probe(struct platform_device *pdev)
                        BD718XX_REG_REGLOCK);
        }
 
+       /* At poweroff transition PMIC HW disables EN bit for regulators but
+        * leaves SEL bit untouched. So if state transition from POWEROFF
+        * is done to SNVS - then all power rails controlled by SW (having
+        * SEL bit set) stay disabled as EN is cleared. This may result boot
+        * failure if any crucial systems are powered by these rails.
+        *
+        * Change the next stage from poweroff to be READY instead of SNVS
+        * for all reset types because OTP loading at READY will clear SEL
+        * bit allowing HW defaults for power rails to be used
+        */
+       err = regmap_update_bits(mfd->regmap, BD718XX_REG_TRANS_COND1,
+                                BD718XX_ON_REQ_POWEROFF_MASK |
+                                BD718XX_SWRESET_POWEROFF_MASK |
+                                BD718XX_WDOG_POWEROFF_MASK |
+                                BD718XX_KEY_L_POWEROFF_MASK,
+                                BD718XX_POWOFF_TO_RDY);
+       if (err) {
+               dev_err(&pdev->dev, "Failed to change reset target\n");
+               goto err;
+       } else {
+               dev_dbg(&pdev->dev, "Changed all resets from SVNS to READY\n");
+       }
+
        for (i = 0; i < pmic_regulators[mfd->chip_type].r_amount; i++) {
 
                const struct regulator_desc *desc;
                struct regulator_dev *rdev;
                const struct bd718xx_regulator_data *r;
 
-               r = &(*pmic_regulators[mfd->chip_type].r_datas)[i];
+               r = &pmic_regulators[mfd->chip_type].r_datas[i];
                desc = &r->desc;
 
                config.dev = pdev->dev.parent;
index 274c5ed..e12dd1f 100644 (file)
@@ -1,17 +1,9 @@
+// SPDX-License-Identifier: GPL-2.0
 /*
  * ROHM BD9571MWV-M regulator driver
  *
  * Copyright (C) 2017 Marek Vasut <marek.vasut+renesas@gmail.com>
  *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License version 2 as
- * published by the Free Software Foundation.
- *
- * This program is distributed "as is" WITHOUT ANY WARRANTY of any
- * kind, whether expressed or implied; without even the implied warranty
- * of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
- * GNU General Public License version 2 for more details.
- *
  * Based on the TPS65086 driver
  *
  * NOTE: VD09 is missing
index 2c66b52..b9d7b45 100644 (file)
@@ -50,6 +50,8 @@
 #define rdev_dbg(rdev, fmt, ...)                                       \
        pr_debug("%s: " fmt, rdev_get_name(rdev), ##__VA_ARGS__)
 
+static DEFINE_WW_CLASS(regulator_ww_class);
+static DEFINE_MUTEX(regulator_nesting_mutex);
 static DEFINE_MUTEX(regulator_list_mutex);
 static LIST_HEAD(regulator_map_list);
 static LIST_HEAD(regulator_ena_gpio_list);
@@ -97,7 +99,7 @@ struct regulator_supply_alias {
 };
 
 static int _regulator_is_enabled(struct regulator_dev *rdev);
-static int _regulator_disable(struct regulator_dev *rdev);
+static int _regulator_disable(struct regulator *regulator);
 static int _regulator_get_voltage(struct regulator_dev *rdev);
 static int _regulator_get_current_limit(struct regulator_dev *rdev);
 static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
@@ -105,6 +107,11 @@ static int _notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data);
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
                                     int min_uV, int max_uV);
+static int regulator_balance_voltage(struct regulator_dev *rdev,
+                                    suspend_state_t state);
+static int regulator_set_voltage_rdev(struct regulator_dev *rdev,
+                                     int min_uV, int max_uV,
+                                     suspend_state_t state);
 static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          struct device *dev,
                                          const char *supply_name);
@@ -149,7 +156,7 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
 /**
  * regulator_lock_nested - lock a single regulator
  * @rdev:              regulator source
- * @subclass:          mutex subclass used for lockdep
+ * @ww_ctx:            w/w mutex acquire context
  *
  * This function can be called many times by one task on
  * a single regulator and its mutex will be locked only
@@ -157,25 +164,54 @@ static inline struct regulator_dev *rdev_get_supply(struct regulator_dev *rdev)
  * than the one, which initially locked the mutex, it will
  * wait on mutex.
  */
-static void regulator_lock_nested(struct regulator_dev *rdev,
-                                 unsigned int subclass)
+static inline int regulator_lock_nested(struct regulator_dev *rdev,
+                                       struct ww_acquire_ctx *ww_ctx)
 {
-       if (!mutex_trylock(&rdev->mutex)) {
-               if (rdev->mutex_owner == current) {
+       bool lock = false;
+       int ret = 0;
+
+       mutex_lock(&regulator_nesting_mutex);
+
+       if (ww_ctx || !ww_mutex_trylock(&rdev->mutex)) {
+               if (rdev->mutex_owner == current)
                        rdev->ref_cnt++;
-                       return;
+               else
+                       lock = true;
+
+               if (lock) {
+                       mutex_unlock(&regulator_nesting_mutex);
+                       ret = ww_mutex_lock(&rdev->mutex, ww_ctx);
+                       mutex_lock(&regulator_nesting_mutex);
                }
-               mutex_lock_nested(&rdev->mutex, subclass);
+       } else {
+               lock = true;
+       }
+
+       if (lock && ret != -EDEADLK) {
+               rdev->ref_cnt++;
+               rdev->mutex_owner = current;
        }
 
-       rdev->ref_cnt = 1;
-       rdev->mutex_owner = current;
+       mutex_unlock(&regulator_nesting_mutex);
+
+       return ret;
 }
 
-static inline void regulator_lock(struct regulator_dev *rdev)
+/**
+ * regulator_lock - lock a single regulator
+ * @rdev:              regulator source
+ *
+ * This function can be called many times by one task on
+ * a single regulator and its mutex will be locked only
+ * once. If a task, which is calling this function is other
+ * than the one, which initially locked the mutex, it will
+ * wait on mutex.
+ */
+void regulator_lock(struct regulator_dev *rdev)
 {
-       regulator_lock_nested(rdev, 0);
+       regulator_lock_nested(rdev, NULL);
 }
+EXPORT_SYMBOL_GPL(regulator_lock);
 
 /**
  * regulator_unlock - unlock a single regulator
@@ -184,47 +220,191 @@ static inline void regulator_lock(struct regulator_dev *rdev)
  * This function unlocks the mutex when the
  * reference counter reaches 0.
  */
-static void regulator_unlock(struct regulator_dev *rdev)
+void regulator_unlock(struct regulator_dev *rdev)
+{
+       mutex_lock(&regulator_nesting_mutex);
+
+       if (--rdev->ref_cnt == 0) {
+               rdev->mutex_owner = NULL;
+               ww_mutex_unlock(&rdev->mutex);
+       }
+
+       WARN_ON_ONCE(rdev->ref_cnt < 0);
+
+       mutex_unlock(&regulator_nesting_mutex);
+}
+EXPORT_SYMBOL_GPL(regulator_unlock);
+
+static bool regulator_supply_is_couple(struct regulator_dev *rdev)
+{
+       struct regulator_dev *c_rdev;
+       int i;
+
+       for (i = 1; i < rdev->coupling_desc.n_coupled; i++) {
+               c_rdev = rdev->coupling_desc.coupled_rdevs[i];
+
+               if (rdev->supply->rdev == c_rdev)
+                       return true;
+       }
+
+       return false;
+}
+
+static void regulator_unlock_recursive(struct regulator_dev *rdev,
+                                      unsigned int n_coupled)
+{
+       struct regulator_dev *c_rdev;
+       int i;
+
+       for (i = n_coupled; i > 0; i--) {
+               c_rdev = rdev->coupling_desc.coupled_rdevs[i - 1];
+
+               if (!c_rdev)
+                       continue;
+
+               if (c_rdev->supply && !regulator_supply_is_couple(c_rdev))
+                       regulator_unlock_recursive(
+                                       c_rdev->supply->rdev,
+                                       c_rdev->coupling_desc.n_coupled);
+
+               regulator_unlock(c_rdev);
+       }
+}
+
+static int regulator_lock_recursive(struct regulator_dev *rdev,
+                                   struct regulator_dev **new_contended_rdev,
+                                   struct regulator_dev **old_contended_rdev,
+                                   struct ww_acquire_ctx *ww_ctx)
 {
-       if (rdev->ref_cnt != 0) {
-               rdev->ref_cnt--;
+       struct regulator_dev *c_rdev;
+       int i, err;
 
-               if (!rdev->ref_cnt) {
-                       rdev->mutex_owner = NULL;
-                       mutex_unlock(&rdev->mutex);
+       for (i = 0; i < rdev->coupling_desc.n_coupled; i++) {
+               c_rdev = rdev->coupling_desc.coupled_rdevs[i];
+
+               if (!c_rdev)
+                       continue;
+
+               if (c_rdev != *old_contended_rdev) {
+                       err = regulator_lock_nested(c_rdev, ww_ctx);
+                       if (err) {
+                               if (err == -EDEADLK) {
+                                       *new_contended_rdev = c_rdev;
+                                       goto err_unlock;
+                               }
+
+                               /* shouldn't happen */
+                               WARN_ON_ONCE(err != -EALREADY);
+                       }
+               } else {
+                       *old_contended_rdev = NULL;
+               }
+
+               if (c_rdev->supply && !regulator_supply_is_couple(c_rdev)) {
+                       err = regulator_lock_recursive(c_rdev->supply->rdev,
+                                                      new_contended_rdev,
+                                                      old_contended_rdev,
+                                                      ww_ctx);
+                       if (err) {
+                               regulator_unlock(c_rdev);
+                               goto err_unlock;
+                       }
                }
        }
+
+       return 0;
+
+err_unlock:
+       regulator_unlock_recursive(rdev, i);
+
+       return err;
 }
 
 /**
- * regulator_lock_supply - lock a regulator and its supplies
- * @rdev:         regulator source
+ * regulator_unlock_dependent - unlock regulator's suppliers and coupled
+ *                             regulators
+ * @rdev:                      regulator source
+ * @ww_ctx:                    w/w mutex acquire context
+ *
+ * Unlock all regulators related with rdev by coupling or suppling.
  */
-static void regulator_lock_supply(struct regulator_dev *rdev)
+static void regulator_unlock_dependent(struct regulator_dev *rdev,
+                                      struct ww_acquire_ctx *ww_ctx)
 {
-       int i;
-
-       for (i = 0; rdev; rdev = rdev_get_supply(rdev), i++)
-               regulator_lock_nested(rdev, i);
+       regulator_unlock_recursive(rdev, rdev->coupling_desc.n_coupled);
+       ww_acquire_fini(ww_ctx);
 }
 
 /**
- * regulator_unlock_supply - unlock a regulator and its supplies
- * @rdev:         regulator source
+ * regulator_lock_dependent - lock regulator's suppliers and coupled regulators
+ * @rdev:                      regulator source
+ * @ww_ctx:                    w/w mutex acquire context
+ *
+ * This function as a wrapper on regulator_lock_recursive(), which locks
+ * all regulators related with rdev by coupling or suppling.
  */
-static void regulator_unlock_supply(struct regulator_dev *rdev)
+static void regulator_lock_dependent(struct regulator_dev *rdev,
+                                    struct ww_acquire_ctx *ww_ctx)
 {
-       struct regulator *supply;
+       struct regulator_dev *new_contended_rdev = NULL;
+       struct regulator_dev *old_contended_rdev = NULL;
+       int err;
+
+       mutex_lock(&regulator_list_mutex);
+
+       ww_acquire_init(ww_ctx, &regulator_ww_class);
+
+       do {
+               if (new_contended_rdev) {
+                       ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx);
+                       old_contended_rdev = new_contended_rdev;
+                       old_contended_rdev->ref_cnt++;
+               }
+
+               err = regulator_lock_recursive(rdev,
+                                              &new_contended_rdev,
+                                              &old_contended_rdev,
+                                              ww_ctx);
+
+               if (old_contended_rdev)
+                       regulator_unlock(old_contended_rdev);
+
+       } while (err == -EDEADLK);
+
+       ww_acquire_done(ww_ctx);
+
+       mutex_unlock(&regulator_list_mutex);
+}
 
-       while (1) {
-               regulator_unlock(rdev);
-               supply = rdev->supply;
+/**
+ * of_get_child_regulator - get a child regulator device node
+ * based on supply name
+ * @parent: Parent device node
+ * @prop_name: Combination regulator supply name and "-supply"
+ *
+ * Traverse all child nodes.
+ * Extract the child regulator device node corresponding to the supply name.
+ * returns the device node corresponding to the regulator if found, else
+ * returns NULL.
+ */
+static struct device_node *of_get_child_regulator(struct device_node *parent,
+                                                 const char *prop_name)
+{
+       struct device_node *regnode = NULL;
+       struct device_node *child = NULL;
 
-               if (!rdev->supply)
-                       return;
+       for_each_child_of_node(parent, child) {
+               regnode = of_parse_phandle(child, prop_name, 0);
 
-               rdev = supply->rdev;
+               if (!regnode) {
+                       regnode = of_get_child_regulator(child, prop_name);
+                       if (regnode)
+                               return regnode;
+               } else {
+                       return regnode;
+               }
        }
+       return NULL;
 }
 
 /**
@@ -247,6 +427,10 @@ static struct device_node *of_get_regulator(struct device *dev, const char *supp
        regnode = of_parse_phandle(dev->of_node, prop_name, 0);
 
        if (!regnode) {
+               regnode = of_get_child_regulator(dev->of_node, prop_name);
+               if (regnode)
+                       return regnode;
+
                dev_dbg(dev, "Looking up %s property in node %pOF failed\n",
                                prop_name, dev->of_node);
                return NULL;
@@ -582,8 +766,10 @@ static ssize_t regulator_total_uA_show(struct device *dev,
        int uA = 0;
 
        regulator_lock(rdev);
-       list_for_each_entry(regulator, &rdev->consumer_list, list)
-               uA += regulator->uA_load;
+       list_for_each_entry(regulator, &rdev->consumer_list, list) {
+               if (regulator->enable_count)
+                       uA += regulator->uA_load;
+       }
        regulator_unlock(rdev);
        return sprintf(buf, "%d\n", uA);
 }
@@ -738,7 +924,7 @@ static int drms_uA_update(struct regulator_dev *rdev)
        int current_uA = 0, output_uV, input_uV, err;
        unsigned int mode;
 
-       lockdep_assert_held_once(&rdev->mutex);
+       lockdep_assert_held_once(&rdev->mutex.base);
 
        /*
         * first check to see if we can set modes at all, otherwise just
@@ -756,8 +942,10 @@ static int drms_uA_update(struct regulator_dev *rdev)
                return -EINVAL;
 
        /* calc total requested load */
-       list_for_each_entry(sibling, &rdev->consumer_list, list)
-               current_uA += sibling->uA_load;
+       list_for_each_entry(sibling, &rdev->consumer_list, list) {
+               if (sibling->enable_count)
+                       current_uA += sibling->uA_load;
+       }
 
        current_uA += rdev->constraints->system_load;
 
@@ -1156,17 +1344,12 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                        rdev_err(rdev, "failed to set initial mode: %d\n", ret);
                        return ret;
                }
-       }
-
-       /* If the constraints say the regulator should be on at this point
-        * and we have control then make sure it is enabled.
-        */
-       if (rdev->constraints->always_on || rdev->constraints->boot_on) {
-               ret = _regulator_do_enable(rdev);
-               if (ret < 0 && ret != -EINVAL) {
-                       rdev_err(rdev, "failed to enable\n");
-                       return ret;
-               }
+       } else if (rdev->constraints->system_load) {
+               /*
+                * We'll only apply the initial system load if an
+                * initial mode wasn't specified.
+                */
+               drms_uA_update(rdev);
        }
 
        if ((rdev->constraints->ramp_delay || rdev->constraints->ramp_disable)
@@ -1214,6 +1397,27 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                }
        }
 
+       /* If the constraints say the regulator should be on at this point
+        * and we have control then make sure it is enabled.
+        */
+       if (rdev->constraints->always_on || rdev->constraints->boot_on) {
+               if (rdev->supply) {
+                       ret = regulator_enable(rdev->supply);
+                       if (ret < 0) {
+                               _regulator_put(rdev->supply);
+                               rdev->supply = NULL;
+                               return ret;
+                       }
+               }
+
+               ret = _regulator_do_enable(rdev);
+               if (ret < 0 && ret != -EINVAL) {
+                       rdev_err(rdev, "failed to enable\n");
+                       return ret;
+               }
+               rdev->use_count++;
+       }
+
        print_constraints(rdev);
        return 0;
 }
@@ -1628,8 +1832,12 @@ static int regulator_resolve_supply(struct regulator_dev *rdev)
                return ret;
        }
 
-       /* Cascade always-on state to supply */
-       if (_regulator_is_enabled(rdev)) {
+       /*
+        * In set_machine_constraints() we may have turned this regulator on
+        * but we couldn't propagate to the supply if it hadn't been resolved
+        * yet.  Do it now.
+        */
+       if (rdev->use_count) {
                ret = regulator_enable(rdev->supply);
                if (ret < 0) {
                        _regulator_put(rdev->supply);
@@ -1713,6 +1921,16 @@ struct regulator *_regulator_get(struct device *dev, const char *id,
                return regulator;
        }
 
+       mutex_lock(&regulator_list_mutex);
+       ret = (rdev->coupling_desc.n_resolved != rdev->coupling_desc.n_coupled);
+       mutex_unlock(&regulator_list_mutex);
+
+       if (ret != 0) {
+               regulator = ERR_PTR(-EPROBE_DEFER);
+               put_device(&rdev->dev);
+               return regulator;
+       }
+
        ret = regulator_resolve_supply(rdev);
        if (ret < 0) {
                regulator = ERR_PTR(ret);
@@ -1832,6 +2050,9 @@ static void _regulator_put(struct regulator *regulator)
 
        lockdep_assert_held_once(&regulator_list_mutex);
 
+       /* Docs say you must disable before calling regulator_put() */
+       WARN_ON(regulator->enable_count);
+
        rdev = regulator->rdev;
 
        debugfs_remove_recursive(regulator->debugfs);
@@ -2225,34 +2446,109 @@ static int _regulator_do_enable(struct regulator_dev *rdev)
        return 0;
 }
 
+/**
+ * _regulator_handle_consumer_enable - handle that a consumer enabled
+ * @regulator: regulator source
+ *
+ * Some things on a regulator consumer (like the contribution towards total
+ * load on the regulator) only have an effect when the consumer wants the
+ * regulator enabled.  Explained in example with two consumers of the same
+ * regulator:
+ *   consumer A: set_load(100);       => total load = 0
+ *   consumer A: regulator_enable();  => total load = 100
+ *   consumer B: set_load(1000);      => total load = 100
+ *   consumer B: regulator_enable();  => total load = 1100
+ *   consumer A: regulator_disable(); => total_load = 1000
+ *
+ * This function (together with _regulator_handle_consumer_disable) is
+ * responsible for keeping track of the refcount for a given regulator consumer
+ * and applying / unapplying these things.
+ *
+ * Returns 0 upon no error; -error upon error.
+ */
+static int _regulator_handle_consumer_enable(struct regulator *regulator)
+{
+       struct regulator_dev *rdev = regulator->rdev;
+
+       lockdep_assert_held_once(&rdev->mutex.base);
+
+       regulator->enable_count++;
+       if (regulator->uA_load && regulator->enable_count == 1)
+               return drms_uA_update(rdev);
+
+       return 0;
+}
+
+/**
+ * _regulator_handle_consumer_disable - handle that a consumer disabled
+ * @regulator: regulator source
+ *
+ * The opposite of _regulator_handle_consumer_enable().
+ *
+ * Returns 0 upon no error; -error upon error.
+ */
+static int _regulator_handle_consumer_disable(struct regulator *regulator)
+{
+       struct regulator_dev *rdev = regulator->rdev;
+
+       lockdep_assert_held_once(&rdev->mutex.base);
+
+       if (!regulator->enable_count) {
+               rdev_err(rdev, "Underflow of regulator enable count\n");
+               return -EINVAL;
+       }
+
+       regulator->enable_count--;
+       if (regulator->uA_load && regulator->enable_count == 0)
+               return drms_uA_update(rdev);
+
+       return 0;
+}
+
 /* locks held by regulator_enable() */
-static int _regulator_enable(struct regulator_dev *rdev)
+static int _regulator_enable(struct regulator *regulator)
 {
+       struct regulator_dev *rdev = regulator->rdev;
        int ret;
 
-       lockdep_assert_held_once(&rdev->mutex);
+       lockdep_assert_held_once(&rdev->mutex.base);
 
-       /* check voltage and requested load before enabling */
-       if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS))
-               drms_uA_update(rdev);
+       if (rdev->use_count == 0 && rdev->supply) {
+               ret = _regulator_enable(rdev->supply);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* balance only if there are regulators coupled */
+       if (rdev->coupling_desc.n_coupled > 1) {
+               ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON);
+               if (ret < 0)
+                       goto err_disable_supply;
+       }
+
+       ret = _regulator_handle_consumer_enable(regulator);
+       if (ret < 0)
+               goto err_disable_supply;
 
        if (rdev->use_count == 0) {
                /* The regulator may on if it's not switchable or left on */
                ret = _regulator_is_enabled(rdev);
                if (ret == -EINVAL || ret == 0) {
                        if (!regulator_ops_is_valid(rdev,
-                                       REGULATOR_CHANGE_STATUS))
-                               return -EPERM;
+                                       REGULATOR_CHANGE_STATUS)) {
+                               ret = -EPERM;
+                               goto err_consumer_disable;
+                       }
 
                        ret = _regulator_do_enable(rdev);
                        if (ret < 0)
-                               return ret;
+                               goto err_consumer_disable;
 
                        _notifier_call_chain(rdev, REGULATOR_EVENT_ENABLE,
                                             NULL);
                } else if (ret < 0) {
                        rdev_err(rdev, "is_enabled() failed: %d\n", ret);
-                       return ret;
+                       goto err_consumer_disable;
                }
                /* Fallthrough on positive return values - already enabled */
        }
@@ -2260,6 +2556,15 @@ static int _regulator_enable(struct regulator_dev *rdev)
        rdev->use_count++;
 
        return 0;
+
+err_consumer_disable:
+       _regulator_handle_consumer_disable(regulator);
+
+err_disable_supply:
+       if (rdev->use_count == 0 && rdev->supply)
+               _regulator_disable(rdev->supply);
+
+       return ret;
 }
 
 /**
@@ -2276,23 +2581,12 @@ static int _regulator_enable(struct regulator_dev *rdev)
 int regulator_enable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       int ret = 0;
-
-       if (regulator->always_on)
-               return 0;
-
-       if (rdev->supply) {
-               ret = regulator_enable(rdev->supply);
-               if (ret != 0)
-                       return ret;
-       }
-
-       mutex_lock(&rdev->mutex);
-       ret = _regulator_enable(rdev);
-       mutex_unlock(&rdev->mutex);
+       struct ww_acquire_ctx ww_ctx;
+       int ret;
 
-       if (ret != 0 && rdev->supply)
-               regulator_disable(rdev->supply);
+       regulator_lock_dependent(rdev, &ww_ctx);
+       ret = _regulator_enable(regulator);
+       regulator_unlock_dependent(rdev, &ww_ctx);
 
        return ret;
 }
@@ -2330,11 +2624,12 @@ static int _regulator_do_disable(struct regulator_dev *rdev)
 }
 
 /* locks held by regulator_disable() */
-static int _regulator_disable(struct regulator_dev *rdev)
+static int _regulator_disable(struct regulator *regulator)
 {
+       struct regulator_dev *rdev = regulator->rdev;
        int ret = 0;
 
-       lockdep_assert_held_once(&rdev->mutex);
+       lockdep_assert_held_once(&rdev->mutex.base);
 
        if (WARN(rdev->use_count <= 0,
                 "unbalanced disables for %s\n", rdev_get_name(rdev)))
@@ -2366,12 +2661,18 @@ static int _regulator_disable(struct regulator_dev *rdev)
 
                rdev->use_count = 0;
        } else if (rdev->use_count > 1) {
-               if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_DRMS))
-                       drms_uA_update(rdev);
-
                rdev->use_count--;
        }
 
+       if (ret == 0)
+               ret = _regulator_handle_consumer_disable(regulator);
+
+       if (ret == 0 && rdev->coupling_desc.n_coupled > 1)
+               ret = regulator_balance_voltage(rdev, PM_SUSPEND_ON);
+
+       if (ret == 0 && rdev->use_count == 0 && rdev->supply)
+               ret = _regulator_disable(rdev->supply);
+
        return ret;
 }
 
@@ -2390,17 +2691,12 @@ static int _regulator_disable(struct regulator_dev *rdev)
 int regulator_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       int ret = 0;
-
-       if (regulator->always_on)
-               return 0;
-
-       mutex_lock(&rdev->mutex);
-       ret = _regulator_disable(rdev);
-       mutex_unlock(&rdev->mutex);
+       struct ww_acquire_ctx ww_ctx;
+       int ret;
 
-       if (ret == 0 && rdev->supply)
-               regulator_disable(rdev->supply);
+       regulator_lock_dependent(rdev, &ww_ctx);
+       ret = _regulator_disable(regulator);
+       regulator_unlock_dependent(rdev, &ww_ctx);
 
        return ret;
 }
@@ -2411,7 +2707,7 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
 {
        int ret = 0;
 
-       lockdep_assert_held_once(&rdev->mutex);
+       lockdep_assert_held_once(&rdev->mutex.base);
 
        ret = _notifier_call_chain(rdev, REGULATOR_EVENT_FORCE_DISABLE |
                        REGULATOR_EVENT_PRE_DISABLE, NULL);
@@ -2444,16 +2740,25 @@ static int _regulator_force_disable(struct regulator_dev *rdev)
 int regulator_force_disable(struct regulator *regulator)
 {
        struct regulator_dev *rdev = regulator->rdev;
+       struct ww_acquire_ctx ww_ctx;
        int ret;
 
-       mutex_lock(&rdev->mutex);
-       regulator->uA_load = 0;
+       regulator_lock_dependent(rdev, &ww_ctx);
+
        ret = _regulator_force_disable(regulator->rdev);
-       mutex_unlock(&rdev->mutex);
 
-       if (rdev->supply)
-               while (rdev->open_count--)
-                       regulator_disable(rdev->supply);
+       if (rdev->coupling_desc.n_coupled > 1)
+               regulator_balance_voltage(rdev, PM_SUSPEND_ON);
+
+       if (regulator->uA_load) {
+               regulator->uA_load = 0;
+               ret = drms_uA_update(rdev);
+       }
+
+       if (rdev->use_count != 0 && rdev->supply)
+               _regulator_disable(rdev->supply);
+
+       regulator_unlock_dependent(rdev, &ww_ctx);
 
        return ret;
 }
@@ -2463,14 +2768,12 @@ static void regulator_disable_work(struct work_struct *work)
 {
        struct regulator_dev *rdev = container_of(work, struct regulator_dev,
                                                  disable_work.work);
+       struct ww_acquire_ctx ww_ctx;
        int count, i, ret;
+       struct regulator *regulator;
+       int total_count = 0;
 
-       regulator_lock(rdev);
-
-       BUG_ON(!rdev->deferred_disables);
-
-       count = rdev->deferred_disables;
-       rdev->deferred_disables = 0;
+       regulator_lock_dependent(rdev, &ww_ctx);
 
        /*
         * Workqueue functions queue the new work instance while the previous
@@ -2480,23 +2783,27 @@ static void regulator_disable_work(struct work_struct *work)
         */
        cancel_delayed_work(&rdev->disable_work);
 
-       for (i = 0; i < count; i++) {
-               ret = _regulator_disable(rdev);
-               if (ret != 0)
-                       rdev_err(rdev, "Deferred disable failed: %d\n", ret);
-       }
+       list_for_each_entry(regulator, &rdev->consumer_list, list) {
+               count = regulator->deferred_disables;
 
-       regulator_unlock(rdev);
+               if (!count)
+                       continue;
+
+               total_count += count;
+               regulator->deferred_disables = 0;
 
-       if (rdev->supply) {
                for (i = 0; i < count; i++) {
-                       ret = regulator_disable(rdev->supply);
-                       if (ret != 0) {
-                               rdev_err(rdev,
-                                        "Supply disable failed: %d\n", ret);
-                       }
+                       ret = _regulator_disable(regulator);
+                       if (ret != 0)
+                               rdev_err(rdev, "Deferred disable failed: %d\n", ret);
                }
        }
+       WARN_ON(!total_count);
+
+       if (rdev->coupling_desc.n_coupled > 1)
+               regulator_balance_voltage(rdev, PM_SUSPEND_ON);
+
+       regulator_unlock_dependent(rdev, &ww_ctx);
 }
 
 /**
@@ -2515,14 +2822,11 @@ int regulator_disable_deferred(struct regulator *regulator, int ms)
 {
        struct regulator_dev *rdev = regulator->rdev;
 
-       if (regulator->always_on)
-               return 0;
-
        if (!ms)
                return regulator_disable(regulator);
 
        regulator_lock(rdev);
-       rdev->deferred_disables++;
+       regulator->deferred_disables++;
        mod_delayed_work(system_power_efficient_wq, &rdev->disable_work,
                         msecs_to_jiffies(ms));
        regulator_unlock(rdev);
@@ -2597,9 +2901,9 @@ int regulator_is_enabled(struct regulator *regulator)
        if (regulator->always_on)
                return 1;
 
-       mutex_lock(&regulator->rdev->mutex);
+       regulator_lock(regulator->rdev);
        ret = _regulator_is_enabled(regulator->rdev);
-       mutex_unlock(&regulator->rdev->mutex);
+       regulator_unlock(regulator->rdev);
 
        return ret;
 }
@@ -3013,8 +3317,6 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
        int ret = 0;
        int old_min_uV, old_max_uV;
        int current_uV;
-       int best_supply_uV = 0;
-       int supply_change_uV = 0;
 
        /* If we're setting the same range as last time the change
         * should be a noop (some cpufreq implementations use the same
@@ -3054,10 +3356,27 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
        voltage->min_uV = min_uV;
        voltage->max_uV = max_uV;
 
-       ret = regulator_check_consumers(rdev, &min_uV, &max_uV, state);
+       /* for not coupled regulators this will just set the voltage */
+       ret = regulator_balance_voltage(rdev, state);
        if (ret < 0)
                goto out2;
 
+out:
+       return 0;
+out2:
+       voltage->min_uV = old_min_uV;
+       voltage->max_uV = old_max_uV;
+
+       return ret;
+}
+
+static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV,
+                                     int max_uV, suspend_state_t state)
+{
+       int best_supply_uV = 0;
+       int supply_change_uV = 0;
+       int ret;
+
        if (rdev->supply &&
            regulator_ops_is_valid(rdev->supply->rdev,
                                   REGULATOR_CHANGE_VOLTAGE) &&
@@ -3069,13 +3388,13 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                selector = regulator_map_voltage(rdev, min_uV, max_uV);
                if (selector < 0) {
                        ret = selector;
-                       goto out2;
+                       goto out;
                }
 
                best_supply_uV = _regulator_list_voltage(rdev, selector, 0);
                if (best_supply_uV < 0) {
                        ret = best_supply_uV;
-                       goto out2;
+                       goto out;
                }
 
                best_supply_uV += rdev->desc->min_dropout_uV;
@@ -3083,7 +3402,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                current_supply_uV = _regulator_get_voltage(rdev->supply->rdev);
                if (current_supply_uV < 0) {
                        ret = current_supply_uV;
-                       goto out2;
+                       goto out;
                }
 
                supply_change_uV = best_supply_uV - current_supply_uV;
@@ -3095,7 +3414,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                if (ret) {
                        dev_err(&rdev->dev, "Failed to increase supply voltage: %d\n",
                                        ret);
-                       goto out2;
+                       goto out;
                }
        }
 
@@ -3105,7 +3424,7 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
                ret = _regulator_do_set_suspend_voltage(rdev, min_uV,
                                                        max_uV, state);
        if (ret < 0)
-               goto out2;
+               goto out;
 
        if (supply_change_uV < 0) {
                ret = regulator_set_voltage_unlocked(rdev->supply,
@@ -3119,10 +3438,273 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
 
 out:
        return ret;
-out2:
-       voltage->min_uV = old_min_uV;
-       voltage->max_uV = old_max_uV;
+}
+
+static int regulator_limit_voltage_step(struct regulator_dev *rdev,
+                                       int *current_uV, int *min_uV)
+{
+       struct regulation_constraints *constraints = rdev->constraints;
+
+       /* Limit voltage change only if necessary */
+       if (!constraints->max_uV_step || !_regulator_is_enabled(rdev))
+               return 1;
+
+       if (*current_uV < 0) {
+               *current_uV = _regulator_get_voltage(rdev);
+
+               if (*current_uV < 0)
+                       return *current_uV;
+       }
+
+       if (abs(*current_uV - *min_uV) <= constraints->max_uV_step)
+               return 1;
+
+       /* Clamp target voltage within the given step */
+       if (*current_uV < *min_uV)
+               *min_uV = min(*current_uV + constraints->max_uV_step,
+                             *min_uV);
+       else
+               *min_uV = max(*current_uV - constraints->max_uV_step,
+                             *min_uV);
+
+       return 0;
+}
+
+static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
+                                        int *current_uV,
+                                        int *min_uV, int *max_uV,
+                                        suspend_state_t state,
+                                        int n_coupled)
+{
+       struct coupling_desc *c_desc = &rdev->coupling_desc;
+       struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
+       struct regulation_constraints *constraints = rdev->constraints;
+       int max_spread = constraints->max_spread;
+       int desired_min_uV = 0, desired_max_uV = INT_MAX;
+       int max_current_uV = 0, min_current_uV = INT_MAX;
+       int highest_min_uV = 0, target_uV, possible_uV;
+       int i, ret;
+       bool done;
+
+       *current_uV = -1;
+
+       /*
+        * If there are no coupled regulators, simply set the voltage
+        * demanded by consumers.
+        */
+       if (n_coupled == 1) {
+               /*
+                * If consumers don't provide any demands, set voltage
+                * to min_uV
+                */
+               desired_min_uV = constraints->min_uV;
+               desired_max_uV = constraints->max_uV;
+
+               ret = regulator_check_consumers(rdev,
+                                               &desired_min_uV,
+                                               &desired_max_uV, state);
+               if (ret < 0)
+                       return ret;
+
+               possible_uV = desired_min_uV;
+               done = true;
+
+               goto finish;
+       }
+
+       /* Find highest min desired voltage */
+       for (i = 0; i < n_coupled; i++) {
+               int tmp_min = 0;
+               int tmp_max = INT_MAX;
+
+               lockdep_assert_held_once(&c_rdevs[i]->mutex.base);
+
+               ret = regulator_check_consumers(c_rdevs[i],
+                                               &tmp_min,
+                                               &tmp_max, state);
+               if (ret < 0)
+                       return ret;
+
+               ret = regulator_check_voltage(c_rdevs[i], &tmp_min, &tmp_max);
+               if (ret < 0)
+                       return ret;
+
+               highest_min_uV = max(highest_min_uV, tmp_min);
+
+               if (i == 0) {
+                       desired_min_uV = tmp_min;
+                       desired_max_uV = tmp_max;
+               }
+       }
+
+       /*
+        * Let target_uV be equal to the desired one if possible.
+        * If not, set it to minimum voltage, allowed by other coupled
+        * regulators.
+        */
+       target_uV = max(desired_min_uV, highest_min_uV - max_spread);
+
+       /*
+        * Find min and max voltages, which currently aren't violating
+        * max_spread.
+        */
+       for (i = 1; i < n_coupled; i++) {
+               int tmp_act;
+
+               if (!_regulator_is_enabled(c_rdevs[i]))
+                       continue;
+
+               tmp_act = _regulator_get_voltage(c_rdevs[i]);
+               if (tmp_act < 0)
+                       return tmp_act;
+
+               min_current_uV = min(tmp_act, min_current_uV);
+               max_current_uV = max(tmp_act, max_current_uV);
+       }
+
+       /* There aren't any other regulators enabled */
+       if (max_current_uV == 0) {
+               possible_uV = target_uV;
+       } else {
+               /*
+                * Correct target voltage, so as it currently isn't
+                * violating max_spread
+                */
+               possible_uV = max(target_uV, max_current_uV - max_spread);
+               possible_uV = min(possible_uV, min_current_uV + max_spread);
+       }
+
+       if (possible_uV > desired_max_uV)
+               return -EINVAL;
+
+       done = (possible_uV == target_uV);
+       desired_min_uV = possible_uV;
 
+finish:
+       /* Apply max_uV_step constraint if necessary */
+       if (state == PM_SUSPEND_ON) {
+               ret = regulator_limit_voltage_step(rdev, current_uV,
+                                                  &desired_min_uV);
+               if (ret < 0)
+                       return ret;
+
+               if (ret == 0)
+                       done = false;
+       }
+
+       /* Set current_uV if wasn't done earlier in the code and if necessary */
+       if (n_coupled > 1 && *current_uV == -1) {
+
+               if (_regulator_is_enabled(rdev)) {
+                       ret = _regulator_get_voltage(rdev);
+                       if (ret < 0)
+                               return ret;
+
+                       *current_uV = ret;
+               } else {
+                       *current_uV = desired_min_uV;
+               }
+       }
+
+       *min_uV = desired_min_uV;
+       *max_uV = desired_max_uV;
+
+       return done;
+}
+
+static int regulator_balance_voltage(struct regulator_dev *rdev,
+                                    suspend_state_t state)
+{
+       struct regulator_dev **c_rdevs;
+       struct regulator_dev *best_rdev;
+       struct coupling_desc *c_desc = &rdev->coupling_desc;
+       int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;
+       bool best_c_rdev_done, c_rdev_done[MAX_COUPLED];
+       unsigned int delta, best_delta;
+
+       c_rdevs = c_desc->coupled_rdevs;
+       n_coupled = c_desc->n_coupled;
+
+       /*
+        * If system is in a state other than PM_SUSPEND_ON, don't check
+        * other coupled regulators.
+        */
+       if (state != PM_SUSPEND_ON)
+               n_coupled = 1;
+
+       if (c_desc->n_resolved < n_coupled) {
+               rdev_err(rdev, "Not all coupled regulators registered\n");
+               return -EPERM;
+       }
+
+       for (i = 0; i < n_coupled; i++)
+               c_rdev_done[i] = false;
+
+       /*
+        * Find the best possible voltage change on each loop. Leave the loop
+        * if there isn't any possible change.
+        */
+       do {
+               best_c_rdev_done = false;
+               best_delta = 0;
+               best_min_uV = 0;
+               best_max_uV = 0;
+               best_c_rdev = 0;
+               best_rdev = NULL;
+
+               /*
+                * Find highest difference between optimal voltage
+                * and current voltage.
+                */
+               for (i = 0; i < n_coupled; i++) {
+                       /*
+                        * optimal_uV is the best voltage that can be set for
+                        * i-th regulator at the moment without violating
+                        * max_spread constraint in order to balance
+                        * the coupled voltages.
+                        */
+                       int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;
+
+                       if (c_rdev_done[i])
+                               continue;
+
+                       ret = regulator_get_optimal_voltage(c_rdevs[i],
+                                                           &current_uV,
+                                                           &optimal_uV,
+                                                           &optimal_max_uV,
+                                                           state, n_coupled);
+                       if (ret < 0)
+                               goto out;
+
+                       delta = abs(optimal_uV - current_uV);
+
+                       if (delta && best_delta <= delta) {
+                               best_c_rdev_done = ret;
+                               best_delta = delta;
+                               best_rdev = c_rdevs[i];
+                               best_min_uV = optimal_uV;
+                               best_max_uV = optimal_max_uV;
+                               best_c_rdev = i;
+                       }
+               }
+
+               /* Nothing to change, return successfully */
+               if (!best_rdev) {
+                       ret = 0;
+                       goto out;
+               }
+
+               ret = regulator_set_voltage_rdev(best_rdev, best_min_uV,
+                                                best_max_uV, state);
+
+               if (ret < 0)
+                       goto out;
+
+               c_rdev_done[best_c_rdev] = best_c_rdev_done;
+
+       } while (n_coupled > 1);
+
+out:
        return ret;
 }
 
@@ -3146,14 +3728,15 @@ out2:
  */
 int regulator_set_voltage(struct regulator *regulator, int min_uV, int max_uV)
 {
-       int ret = 0;
+       struct ww_acquire_ctx ww_ctx;
+       int ret;
 
-       regulator_lock_supply(regulator->rdev);
+       regulator_lock_dependent(regulator->rdev, &ww_ctx);
 
        ret = regulator_set_voltage_unlocked(regulator, min_uV, max_uV,
                                             PM_SUSPEND_ON);
 
-       regulator_unlock_supply(regulator->rdev);
+       regulator_unlock_dependent(regulator->rdev, &ww_ctx);
 
        return ret;
 }
@@ -3225,18 +3808,19 @@ static int _regulator_set_suspend_voltage(struct regulator *regulator,
 int regulator_set_suspend_voltage(struct regulator *regulator, int min_uV,
                                  int max_uV, suspend_state_t state)
 {
-       int ret = 0;
+       struct ww_acquire_ctx ww_ctx;
+       int ret;
 
        /* 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);
+       regulator_lock_dependent(regulator->rdev, &ww_ctx);
 
        ret = _regulator_set_suspend_voltage(regulator, min_uV,
                                             max_uV, state);
 
-       regulator_unlock_supply(regulator->rdev);
+       regulator_unlock_dependent(regulator->rdev, &ww_ctx);
 
        return ret;
 }
@@ -3426,13 +4010,12 @@ static int _regulator_get_voltage(struct regulator_dev *rdev)
  */
 int regulator_get_voltage(struct regulator *regulator)
 {
+       struct ww_acquire_ctx ww_ctx;
        int ret;
 
-       regulator_lock_supply(regulator->rdev);
-
+       regulator_lock_dependent(regulator->rdev, &ww_ctx);
        ret = _regulator_get_voltage(regulator->rdev);
-
-       regulator_unlock_supply(regulator->rdev);
+       regulator_unlock_dependent(regulator->rdev, &ww_ctx);
 
        return ret;
 }
@@ -3650,16 +4233,30 @@ EXPORT_SYMBOL_GPL(regulator_get_error_flags);
  * DRMS will sum the total requested load on the regulator and change
  * to the most efficient operating mode if platform constraints allow.
  *
+ * NOTE: when a regulator consumer requests to have a regulator
+ * disabled then any load that consumer requested no longer counts
+ * toward the total requested load.  If the regulator is re-enabled
+ * then the previously requested load will start counting again.
+ *
+ * If a regulator is an always-on regulator then an individual consumer's
+ * load will still be removed if that consumer is fully disabled.
+ *
  * On error a negative errno is returned.
  */
 int regulator_set_load(struct regulator *regulator, int uA_load)
 {
        struct regulator_dev *rdev = regulator->rdev;
-       int ret;
+       int old_uA_load;
+       int ret = 0;
 
        regulator_lock(rdev);
+       old_uA_load = regulator->uA_load;
        regulator->uA_load = uA_load;
-       ret = drms_uA_update(rdev);
+       if (regulator->enable_count && old_uA_load != uA_load) {
+               ret = drms_uA_update(rdev);
+               if (ret < 0)
+                       regulator->uA_load = old_uA_load;
+       }
        regulator_unlock(rdev);
 
        return ret;
@@ -3830,11 +4427,8 @@ int regulator_bulk_enable(int num_consumers,
        int ret = 0;
 
        for (i = 0; i < num_consumers; i++) {
-               if (consumers[i].consumer->always_on)
-                       consumers[i].ret = 0;
-               else
-                       async_schedule_domain(regulator_bulk_enable_async,
-                                             &consumers[i], &async_domain);
+               async_schedule_domain(regulator_bulk_enable_async,
+                                     &consumers[i], &async_domain);
        }
 
        async_synchronize_full_domain(&async_domain);
@@ -3968,7 +4562,7 @@ EXPORT_SYMBOL_GPL(regulator_bulk_free);
 int regulator_notifier_call_chain(struct regulator_dev *rdev,
                                  unsigned long event, void *data)
 {
-       lockdep_assert_held_once(&rdev->mutex);
+       lockdep_assert_held_once(&rdev->mutex.base);
 
        _notifier_call_chain(rdev, event, data);
        return NOTIFY_DONE;
@@ -4070,10 +4664,6 @@ static umode_t regulator_attr_is_visible(struct kobject *kobj,
        if (attr == &dev_attr_bypass.attr)
                return ops->get_bypass ? mode : 0;
 
-       /* some attributes are type-specific */
-       if (attr == &dev_attr_requested_microamps.attr)
-               return rdev->desc->type == REGULATOR_CURRENT ? mode : 0;
-
        /* constraints need specific supporting methods */
        if (attr == &dev_attr_min_microvolts.attr ||
            attr == &dev_attr_max_microvolts.attr)
@@ -4157,7 +4747,7 @@ static int regulator_register_resolve_supply(struct device *dev, void *data)
        return 0;
 }
 
-static int regulator_fill_coupling_array(struct regulator_dev *rdev)
+static void regulator_resolve_coupling(struct regulator_dev *rdev)
 {
        struct coupling_desc *c_desc = &rdev->coupling_desc;
        int n_coupled = c_desc->n_coupled;
@@ -4171,33 +4761,58 @@ static int regulator_fill_coupling_array(struct regulator_dev *rdev)
 
                c_rdev = of_parse_coupled_regulator(rdev, i - 1);
 
-               if (c_rdev) {
-                       c_desc->coupled_rdevs[i] = c_rdev;
-                       c_desc->n_resolved++;
-               }
-       }
+               if (!c_rdev)
+                       continue;
 
-       if (rdev->coupling_desc.n_resolved < n_coupled)
-               return -1;
-       else
-               return 0;
+               regulator_lock(c_rdev);
+
+               c_desc->coupled_rdevs[i] = c_rdev;
+               c_desc->n_resolved++;
+
+               regulator_unlock(c_rdev);
+
+               regulator_resolve_coupling(c_rdev);
+       }
 }
 
-static int regulator_register_fill_coupling_array(struct device *dev,
-                                                 void *data)
+static void regulator_remove_coupling(struct regulator_dev *rdev)
 {
-       struct regulator_dev *rdev = dev_to_rdev(dev);
+       struct coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc;
+       struct regulator_dev *__c_rdev, *c_rdev;
+       unsigned int __n_coupled, n_coupled;
+       int i, k;
 
-       if (!IS_ENABLED(CONFIG_OF))
-               return 0;
+       n_coupled = c_desc->n_coupled;
 
-       if (regulator_fill_coupling_array(rdev))
-               rdev_dbg(rdev, "unable to resolve coupling\n");
+       for (i = 1; i < n_coupled; i++) {
+               c_rdev = c_desc->coupled_rdevs[i];
 
-       return 0;
+               if (!c_rdev)
+                       continue;
+
+               regulator_lock(c_rdev);
+
+               __c_desc = &c_rdev->coupling_desc;
+               __n_coupled = __c_desc->n_coupled;
+
+               for (k = 1; k < __n_coupled; k++) {
+                       __c_rdev = __c_desc->coupled_rdevs[k];
+
+                       if (__c_rdev == rdev) {
+                               __c_desc->coupled_rdevs[k] = NULL;
+                               __c_desc->n_resolved--;
+                               break;
+                       }
+               }
+
+               regulator_unlock(c_rdev);
+
+               c_desc->coupled_rdevs[i] = NULL;
+               c_desc->n_resolved--;
+       }
 }
 
-static int regulator_resolve_coupling(struct regulator_dev *rdev)
+static int regulator_init_coupling(struct regulator_dev *rdev)
 {
        int n_phandles;
 
@@ -4237,13 +4852,6 @@ static int regulator_resolve_coupling(struct regulator_dev *rdev)
        if (!of_check_coupling_data(rdev))
                return -EPERM;
 
-       /*
-        * After everything has been checked, try to fill rdevs array
-        * with pointers to regulators parsed from device tree. If some
-        * regulators are not registered yet, retry in late init call
-        */
-       regulator_fill_coupling_array(rdev);
-
        return 0;
 }
 
@@ -4265,21 +4873,33 @@ regulator_register(const struct regulator_desc *regulator_desc,
        struct regulator_config *config = NULL;
        static atomic_t regulator_no = ATOMIC_INIT(-1);
        struct regulator_dev *rdev;
+       bool dangling_cfg_gpiod = false;
+       bool dangling_of_gpiod = false;
        struct device *dev;
        int ret, i;
 
-       if (regulator_desc == NULL || cfg == NULL)
+       if (cfg == NULL)
                return ERR_PTR(-EINVAL);
+       if (cfg->ena_gpiod)
+               dangling_cfg_gpiod = true;
+       if (regulator_desc == NULL) {
+               ret = -EINVAL;
+               goto rinse;
+       }
 
        dev = cfg->dev;
        WARN_ON(!dev);
 
-       if (regulator_desc->name == NULL || regulator_desc->ops == NULL)
-               return ERR_PTR(-EINVAL);
+       if (regulator_desc->name == NULL || regulator_desc->ops == NULL) {
+               ret = -EINVAL;
+               goto rinse;
+       }
 
        if (regulator_desc->type != REGULATOR_VOLTAGE &&
-           regulator_desc->type != REGULATOR_CURRENT)
-               return ERR_PTR(-EINVAL);
+           regulator_desc->type != REGULATOR_CURRENT) {
+               ret = -EINVAL;
+               goto rinse;
+       }
 
        /* Only one of each should be implemented */
        WARN_ON(regulator_desc->ops->get_voltage &&
@@ -4290,16 +4910,20 @@ regulator_register(const struct regulator_desc *regulator_desc,
        /* If we're using selectors we must implement list_voltage. */
        if (regulator_desc->ops->get_voltage_sel &&
            !regulator_desc->ops->list_voltage) {
-               return ERR_PTR(-EINVAL);
+               ret = -EINVAL;
+               goto rinse;
        }
        if (regulator_desc->ops->set_voltage_sel &&
            !regulator_desc->ops->list_voltage) {
-               return ERR_PTR(-EINVAL);
+               ret = -EINVAL;
+               goto rinse;
        }
 
        rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
-       if (rdev == NULL)
-               return ERR_PTR(-ENOMEM);
+       if (rdev == NULL) {
+               ret = -ENOMEM;
+               goto rinse;
+       }
 
        /*
         * Duplicate the config so the driver could override it after
@@ -4308,17 +4932,28 @@ regulator_register(const struct regulator_desc *regulator_desc,
        config = kmemdup(cfg, sizeof(*cfg), GFP_KERNEL);
        if (config == NULL) {
                kfree(rdev);
-               return ERR_PTR(-ENOMEM);
+               ret = -ENOMEM;
+               goto rinse;
        }
 
        init_data = regulator_of_get_init_data(dev, regulator_desc, config,
                                               &rdev->dev.of_node);
+       /*
+        * We need to keep track of any GPIO descriptor coming from the
+        * device tree until we have handled it over to the core. If the
+        * config that was passed in to this function DOES NOT contain
+        * a descriptor, and the config after this call DOES contain
+        * a descriptor, we definately got one from parsing the device
+        * tree.
+        */
+       if (!cfg->ena_gpiod && config->ena_gpiod)
+               dangling_of_gpiod = true;
        if (!init_data) {
                init_data = config->init_data;
                rdev->dev.of_node = of_node_get(config->of_node);
        }
 
-       mutex_init(&rdev->mutex);
+       ww_mutex_init(&rdev->mutex, &regulator_ww_class);
        rdev->reg_data = config->driver_data;
        rdev->owner = regulator_desc->owner;
        rdev->desc = regulator_desc;
@@ -4351,6 +4986,9 @@ regulator_register(const struct regulator_desc *regulator_desc,
                                 config->ena_gpio, ret);
                        goto clean;
                }
+               /* The regulator core took over the GPIO descriptor */
+               dangling_cfg_gpiod = false;
+               dangling_of_gpiod = false;
        }
 
        /* register with sysfs */
@@ -4380,11 +5018,8 @@ regulator_register(const struct regulator_desc *regulator_desc,
        if (ret < 0)
                goto wash;
 
-       mutex_lock(&regulator_list_mutex);
-       ret = regulator_resolve_coupling(rdev);
-       mutex_unlock(&regulator_list_mutex);
-
-       if (ret != 0)
+       ret = regulator_init_coupling(rdev);
+       if (ret < 0)
                goto wash;
 
        /* add consumers devices */
@@ -4418,6 +5053,11 @@ regulator_register(const struct regulator_desc *regulator_desc,
 
        rdev_init_debugfs(rdev);
 
+       /* try to resolve regulators coupling since a new one was registered */
+       mutex_lock(&regulator_list_mutex);
+       regulator_resolve_coupling(rdev);
+       mutex_unlock(&regulator_list_mutex);
+
        /* try to resolve regulators supply since a new one was registered */
        class_for_each_device(&regulator_class, NULL, NULL,
                              regulator_register_resolve_supply);
@@ -4434,8 +5074,13 @@ wash:
        regulator_ena_gpio_free(rdev);
        mutex_unlock(&regulator_list_mutex);
 clean:
+       if (dangling_of_gpiod)
+               gpiod_put(config->ena_gpiod);
        kfree(rdev);
        kfree(config);
+rinse:
+       if (dangling_cfg_gpiod)
+               gpiod_put(cfg->ena_gpiod);
        return ERR_PTR(ret);
 }
 EXPORT_SYMBOL_GPL(regulator_register);
@@ -4456,15 +5101,19 @@ void regulator_unregister(struct regulator_dev *rdev)
                        regulator_disable(rdev->supply);
                regulator_put(rdev->supply);
        }
+
        mutex_lock(&regulator_list_mutex);
+
        debugfs_remove_recursive(rdev->debugfs);
        flush_work(&rdev->disable_work.work);
        WARN_ON(rdev->open_count);
+       regulator_remove_coupling(rdev);
        unset_regulator_supplies(rdev);
        list_del(&rdev->list);
        regulator_ena_gpio_free(rdev);
-       mutex_unlock(&regulator_list_mutex);
        device_unregister(&rdev->dev);
+
+       mutex_unlock(&regulator_list_mutex);
 }
 EXPORT_SYMBOL_GPL(regulator_unregister);
 
@@ -4621,23 +5270,8 @@ static int supply_map_show(struct seq_file *sf, void *data)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(supply_map);
 
-static int supply_map_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, supply_map_show, inode->i_private);
-}
-#endif
-
-static const struct file_operations supply_map_fops = {
-#ifdef CONFIG_DEBUG_FS
-       .open = supply_map_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-#endif
-};
-
-#ifdef CONFIG_DEBUG_FS
 struct summary_data {
        struct seq_file *s;
        struct regulator_dev *parent;
@@ -4672,8 +5306,6 @@ static void regulator_summary_show_subtree(struct seq_file *s,
        if (!rdev)
                return;
 
-       regulator_lock_nested(rdev, level);
-
        opmode = _regulator_get_mode_unlocked(rdev);
        seq_printf(s, "%*s%-*s %3d %4d %6d %7s ",
                   level * 3 + 1, "",
@@ -4712,8 +5344,11 @@ static void regulator_summary_show_subtree(struct seq_file *s,
 
                switch (rdev->desc->type) {
                case REGULATOR_VOLTAGE:
-                       seq_printf(s, "%37dmA %5dmV %5dmV",
+                       seq_printf(s, "%3d %33dmA%c%5dmV %5dmV",
+                                  consumer->enable_count,
                                   consumer->uA_load / 1000,
+                                  consumer->uA_load && !consumer->enable_count ?
+                                  '*' : ' ',
                                   consumer->voltage[PM_SUSPEND_ON].min_uV / 1000,
                                   consumer->voltage[PM_SUSPEND_ON].max_uV / 1000);
                        break;
@@ -4730,8 +5365,105 @@ static void regulator_summary_show_subtree(struct seq_file *s,
 
        class_for_each_device(&regulator_class, NULL, &summary_data,
                              regulator_summary_show_children);
+}
+
+struct summary_lock_data {
+       struct ww_acquire_ctx *ww_ctx;
+       struct regulator_dev **new_contended_rdev;
+       struct regulator_dev **old_contended_rdev;
+};
+
+static int regulator_summary_lock_one(struct device *dev, void *data)
+{
+       struct regulator_dev *rdev = dev_to_rdev(dev);
+       struct summary_lock_data *lock_data = data;
+       int ret = 0;
+
+       if (rdev != *lock_data->old_contended_rdev) {
+               ret = regulator_lock_nested(rdev, lock_data->ww_ctx);
+
+               if (ret == -EDEADLK)
+                       *lock_data->new_contended_rdev = rdev;
+               else
+                       WARN_ON_ONCE(ret);
+       } else {
+               *lock_data->old_contended_rdev = NULL;
+       }
+
+       return ret;
+}
+
+static int regulator_summary_unlock_one(struct device *dev, void *data)
+{
+       struct regulator_dev *rdev = dev_to_rdev(dev);
+       struct summary_lock_data *lock_data = data;
+
+       if (lock_data) {
+               if (rdev == *lock_data->new_contended_rdev)
+                       return -EDEADLK;
+       }
 
        regulator_unlock(rdev);
+
+       return 0;
+}
+
+static int regulator_summary_lock_all(struct ww_acquire_ctx *ww_ctx,
+                                     struct regulator_dev **new_contended_rdev,
+                                     struct regulator_dev **old_contended_rdev)
+{
+       struct summary_lock_data lock_data;
+       int ret;
+
+       lock_data.ww_ctx = ww_ctx;
+       lock_data.new_contended_rdev = new_contended_rdev;
+       lock_data.old_contended_rdev = old_contended_rdev;
+
+       ret = class_for_each_device(&regulator_class, NULL, &lock_data,
+                                   regulator_summary_lock_one);
+       if (ret)
+               class_for_each_device(&regulator_class, NULL, &lock_data,
+                                     regulator_summary_unlock_one);
+
+       return ret;
+}
+
+static void regulator_summary_lock(struct ww_acquire_ctx *ww_ctx)
+{
+       struct regulator_dev *new_contended_rdev = NULL;
+       struct regulator_dev *old_contended_rdev = NULL;
+       int err;
+
+       mutex_lock(&regulator_list_mutex);
+
+       ww_acquire_init(ww_ctx, &regulator_ww_class);
+
+       do {
+               if (new_contended_rdev) {
+                       ww_mutex_lock_slow(&new_contended_rdev->mutex, ww_ctx);
+                       old_contended_rdev = new_contended_rdev;
+                       old_contended_rdev->ref_cnt++;
+               }
+
+               err = regulator_summary_lock_all(ww_ctx,
+                                                &new_contended_rdev,
+                                                &old_contended_rdev);
+
+               if (old_contended_rdev)
+                       regulator_unlock(old_contended_rdev);
+
+       } while (err == -EDEADLK);
+
+       ww_acquire_done(ww_ctx);
+}
+
+static void regulator_summary_unlock(struct ww_acquire_ctx *ww_ctx)
+{
+       class_for_each_device(&regulator_class, NULL, NULL,
+                             regulator_summary_unlock_one);
+       ww_acquire_fini(ww_ctx);
+
+       mutex_unlock(&regulator_list_mutex);
 }
 
 static int regulator_summary_show_roots(struct device *dev, void *data)
@@ -4747,29 +5479,22 @@ static int regulator_summary_show_roots(struct device *dev, void *data)
 
 static int regulator_summary_show(struct seq_file *s, void *data)
 {
+       struct ww_acquire_ctx ww_ctx;
+
        seq_puts(s, " regulator                      use open bypass  opmode voltage current     min     max\n");
        seq_puts(s, "---------------------------------------------------------------------------------------\n");
 
+       regulator_summary_lock(&ww_ctx);
+
        class_for_each_device(&regulator_class, NULL, s,
                              regulator_summary_show_roots);
 
-       return 0;
-}
+       regulator_summary_unlock(&ww_ctx);
 
-static int regulator_summary_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, regulator_summary_show, inode->i_private);
+       return 0;
 }
-#endif
-
-static const struct file_operations regulator_summary_fops = {
-#ifdef CONFIG_DEBUG_FS
-       .open           = regulator_summary_open,
-       .read           = seq_read,
-       .llseek         = seq_lseek,
-       .release        = single_release,
-#endif
-};
+DEFINE_SHOW_ATTRIBUTE(regulator_summary);
+#endif /* CONFIG_DEBUG_FS */
 
 static int __init regulator_init(void)
 {
@@ -4781,12 +5506,13 @@ static int __init regulator_init(void)
        if (!debugfs_root)
                pr_warn("regulator: Failed to create debugfs directory\n");
 
+#ifdef CONFIG_DEBUG_FS
        debugfs_create_file("supply_map", 0444, debugfs_root, NULL,
                            &supply_map_fops);
 
        debugfs_create_file("regulator_summary", 0444, debugfs_root,
                            NULL, &regulator_summary_fops);
-
+#endif
        regulator_dummy_init();
 
        return ret;
@@ -4873,9 +5599,6 @@ static int __init regulator_init_complete(void)
        class_for_each_device(&regulator_class, NULL, NULL,
                              regulator_late_cleanup);
 
-       class_for_each_device(&regulator_class, NULL, NULL,
-                             regulator_register_fill_coupling_array);
-
        return 0;
 }
 late_initcall_sync(regulator_init_complete);
index 37e4025..207cb38 100644 (file)
@@ -435,7 +435,7 @@ static int da9052_regulator_probe(struct platform_device *pdev)
                        return -ENODEV;
 
                for_each_child_of_node(nproot, np) {
-                       if (!of_node_cmp(np->name,
+                       if (of_node_name_eq(np,
                                         regulator->info->reg_desc.name)) {
                                config.init_data = of_get_regulator_init_data(
                                        &pdev->dev, np,
index d0496d6..84dba64 100644 (file)
@@ -131,7 +131,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data)
        if (error < 0)
                goto error_i2c;
 
-       mutex_lock(&chip->rdev->mutex);
+       regulator_lock(chip->rdev);
 
        if (val & DA9210_E_OVCURR) {
                regulator_notifier_call_chain(chip->rdev,
@@ -157,7 +157,7 @@ static irqreturn_t da9210_irq_handler(int irq, void *data)
                handled |= DA9210_E_VMAX;
        }
 
-       mutex_unlock(&chip->rdev->mutex);
+       regulator_unlock(chip->rdev);
 
        if (handled) {
                /* Clear handled events */
index 8f68c7a..109ee12 100644 (file)
@@ -389,6 +389,12 @@ static int da9211_regulator_init(struct da9211 *chip)
                else
                        config.ena_gpiod = NULL;
 
+               /*
+                * Hand the GPIO descriptor management over to the regulator
+                * core, remove it from GPIO devres management.
+                */
+               if (config.ena_gpiod)
+                       devm_gpiod_unhinge(chip->dev, config.ena_gpiod);
                chip->rdev[i] = devm_regulator_register(chip->dev,
                        &da9211_regulators[i], &config);
                if (IS_ERR(chip->rdev[i])) {
index 8976141..308e3ff 100644 (file)
@@ -75,7 +75,7 @@ static struct ux500_regulator_debug {
        u8 *state_after_suspend;
 } rdebug;
 
-static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p)
+static int ux500_regulator_power_state_cnt_show(struct seq_file *s, void *p)
 {
        /* print power state count */
        seq_printf(s, "ux500-regulator power state count: %i\n",
@@ -83,23 +83,9 @@ static int ux500_regulator_power_state_cnt_print(struct seq_file *s, void *p)
 
        return 0;
 }
+DEFINE_SHOW_ATTRIBUTE(ux500_regulator_power_state_cnt);
 
-static int ux500_regulator_power_state_cnt_open(struct inode *inode,
-       struct file *file)
-{
-       return single_open(file, ux500_regulator_power_state_cnt_print,
-               inode->i_private);
-}
-
-static const struct file_operations ux500_regulator_power_state_cnt_fops = {
-       .open = ux500_regulator_power_state_cnt_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
-
-static int ux500_regulator_status_print(struct seq_file *s, void *p)
+static int ux500_regulator_status_show(struct seq_file *s, void *p)
 {
        int i;
 
@@ -122,20 +108,7 @@ static int ux500_regulator_status_print(struct seq_file *s, void *p)
 
        return 0;
 }
-
-static int ux500_regulator_status_open(struct inode *inode, struct file *file)
-{
-       return single_open(file, ux500_regulator_status_print,
-               inode->i_private);
-}
-
-static const struct file_operations ux500_regulator_status_fops = {
-       .open = ux500_regulator_status_open,
-       .read = seq_read,
-       .llseek = seq_lseek,
-       .release = single_release,
-       .owner = THIS_MODULE,
-};
+DEFINE_SHOW_ATTRIBUTE(ux500_regulator_status);
 
 int __attribute__((weak)) dbx500_regulator_testcase(
        struct dbx500_regulator_info *regulator_info,
index ccc2903..9abdb91 100644 (file)
@@ -183,7 +183,11 @@ static int reg_fixed_voltage_probe(struct platform_device *pdev)
         */
        gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE;
 
-       cfg.ena_gpiod = devm_gpiod_get_optional(&pdev->dev, NULL, gflags);
+       /*
+        * Do not use devm* here: the regulator core takes over the
+        * lifecycle management of the GPIO descriptor.
+        */
+       cfg.ena_gpiod = gpiod_get_optional(&pdev->dev, NULL, gflags);
        if (IS_ERR(cfg.ena_gpiod))
                return PTR_ERR(cfg.ena_gpiod);
 
index 943926a..6017f15 100644 (file)
@@ -42,6 +42,8 @@ struct regulator {
        unsigned int always_on:1;
        unsigned int bypass:1;
        int uA_load;
+       unsigned int enable_count;
+       unsigned int deferred_disables;
        struct regulator_voltage voltage[REGULATOR_STATES_NUM];
        const char *supply_name;
        struct device_attribute dev_attr;
index bbedb08..8c0e841 100644 (file)
@@ -224,13 +224,15 @@ static struct gpio_desc *lm363x_regulator_of_get_enable_gpio(struct device *dev,
        /*
         * Check LCM_EN1/2_GPIO is configured.
         * Those pins are used for enabling VPOS/VNEG LDOs.
+        * Do not use devm* here: the regulator core takes over the
+        * lifecycle management of the GPIO descriptor.
         */
        switch (id) {
        case LM3632_LDO_POS:
-               return devm_gpiod_get_index_optional(dev, "enable", 0,
+               return gpiod_get_index_optional(dev, "enable", 0,
                                GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
        case LM3632_LDO_NEG:
-               return devm_gpiod_get_index_optional(dev, "enable", 1,
+               return gpiod_get_index_optional(dev, "enable", 1,
                                GPIOD_OUT_LOW | GPIOD_FLAGS_BIT_NONEXCLUSIVE);
        default:
                return NULL;
@@ -263,6 +265,8 @@ static int lm363x_regulator_probe(struct platform_device *pdev)
                                         LM3632_EXT_EN_MASK,
                                         LM3632_EXT_EN_MASK);
                if (ret) {
+                       if (gpiod)
+                               gpiod_put(gpiod);
                        dev_err(dev, "External pin err: %d\n", ret);
                        return ret;
                }
index 2b5073b..5a89e6d 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/module.h>
 #include <linux/mutex.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
@@ -20,6 +21,8 @@
 #include <linux/regulator/of_regulator.h>
 
 #include <linux/mfd/lochnagar.h>
+#include <linux/mfd/lochnagar1_regs.h>
+#include <linux/mfd/lochnagar2_regs.h>
 
 static const struct regulator_ops lochnagar_micvdd_ops = {
        .enable = regulator_enable_regmap,
@@ -212,28 +215,52 @@ static const struct regulator_desc lochnagar_regulators[] = {
        },
 };
 
+static const struct of_device_id lochnagar_of_match[] = {
+       {
+               .compatible = "cirrus,lochnagar2-micvdd",
+               .data = &lochnagar_regulators[LOCHNAGAR_MICVDD],
+       },
+       {
+               .compatible = "cirrus,lochnagar2-mic1vdd",
+               .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD],
+       },
+       {
+               .compatible = "cirrus,lochnagar2-mic2vdd",
+               .data = &lochnagar_regulators[LOCHNAGAR_MIC1VDD],
+       },
+       {
+               .compatible = "cirrus,lochnagar2-vddcore",
+               .data = &lochnagar_regulators[LOCHNAGAR_VDDCORE],
+       },
+       {},
+};
+
 static int lochnagar_regulator_probe(struct platform_device *pdev)
 {
        struct device *dev = &pdev->dev;
        struct lochnagar *lochnagar = dev_get_drvdata(dev->parent);
        struct regulator_config config = { };
+       const struct of_device_id *of_id;
+       const struct regulator_desc *desc;
        struct regulator_dev *rdev;
-       int ret, i;
+       int ret;
 
-       config.dev = lochnagar->dev;
+       config.dev = dev;
        config.regmap = lochnagar->regmap;
        config.driver_data = lochnagar;
 
-       for (i = 0; i < ARRAY_SIZE(lochnagar_regulators); i++) {
-               const struct regulator_desc *desc = &lochnagar_regulators[i];
+       of_id = of_match_device(lochnagar_of_match, dev);
+       if (!of_id)
+               return -EINVAL;
 
-               rdev = devm_regulator_register(dev, desc, &config);
-               if (IS_ERR(rdev)) {
-                       ret = PTR_ERR(rdev);
-                       dev_err(dev, "Failed to register %s regulator: %d\n",
-                               desc->name, ret);
-                       return ret;
-               }
+       desc = of_id->data;
+
+       rdev = devm_regulator_register(dev, desc, &config);
+       if (IS_ERR(rdev)) {
+               ret = PTR_ERR(rdev);
+               dev_err(dev, "Failed to register %s regulator: %d\n",
+                       desc->name, ret);
+               return ret;
        }
 
        return 0;
@@ -242,6 +269,7 @@ static int lochnagar_regulator_probe(struct platform_device *pdev)
 static struct platform_driver lochnagar_regulator_driver = {
        .driver = {
                .name = "lochnagar-regulator",
+               .of_match_table = of_match_ptr(lochnagar_of_match),
        },
 
        .probe = lochnagar_regulator_probe,
index 553b479..2ee22e7 100644 (file)
@@ -501,8 +501,12 @@ static int lp8788_config_ldo_enable_mode(struct platform_device *pdev,
                return 0;
        }
 
-       /* FIXME: check default mode for GPIO here: high or low? */
-       ldo->ena_gpiod = devm_gpiod_get_index_optional(&pdev->dev,
+       /*
+        * Do not use devm* here: the regulator core takes over the
+        * lifecycle management of the GPIO descriptor.
+        * FIXME: check default mode for GPIO here: high or low?
+        */
+       ldo->ena_gpiod = gpiod_get_index_optional(&pdev->dev,
                                               "enable",
                                               enable_id,
                                               GPIOD_OUT_HIGH |
index bee0609..8020eb5 100644 (file)
@@ -11,8 +11,7 @@
 #include <linux/kernel.h>
 #include <linux/bug.h>
 #include <linux/err.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/slab.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
@@ -76,6 +75,7 @@ enum max77686_ramp_rate {
 };
 
 struct max77686_data {
+       struct device *dev;
        DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
 
        /* Array indexed by regulator id */
@@ -250,26 +250,34 @@ static int max77686_of_parse_cb(struct device_node *np,
                struct regulator_config *config)
 {
        struct max77686_data *max77686 = config->driver_data;
+       int ret;
 
        switch (desc->id) {
        case MAX77686_BUCK8:
        case MAX77686_BUCK9:
        case MAX77686_LDO20 ... MAX77686_LDO22:
-               config->ena_gpio = of_get_named_gpio(np,
-                                       "maxim,ena-gpios", 0);
-               config->ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
-               config->ena_gpio_initialized = true;
+               config->ena_gpiod = gpiod_get_from_of_node(np,
+                               "maxim,ena",
+                               0,
+                               GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+                               "max77686-regulator");
+               if (IS_ERR(config->ena_gpiod))
+                       config->ena_gpiod = NULL;
                break;
        default:
                return 0;
        }
 
-       if (gpio_is_valid(config->ena_gpio)) {
+       if (config->ena_gpiod) {
                set_bit(desc->id, max77686->gpio_enabled);
 
-               return regmap_update_bits(config->regmap, desc->enable_reg,
-                                         desc->enable_mask,
-                                         MAX77686_GPIO_CONTROL);
+               ret = regmap_update_bits(config->regmap, desc->enable_reg,
+                                        desc->enable_mask,
+                                        MAX77686_GPIO_CONTROL);
+               if (ret) {
+                       gpiod_put(config->ena_gpiod);
+                       config->ena_gpiod = NULL;
+               }
        }
 
        return 0;
@@ -507,6 +515,7 @@ static int max77686_pmic_probe(struct platform_device *pdev)
        if (!max77686)
                return -ENOMEM;
 
+       max77686->dev = &pdev->dev;
        config.dev = iodev->dev;
        config.regmap = iodev->regmap;
        config.driver_data = max77686;
index 6c39fff..cf2a291 100644 (file)
@@ -231,9 +231,13 @@ static int max8952_pmic_probe(struct i2c_client *client,
        else
                gflags = GPIOD_OUT_LOW;
        gflags |= GPIOD_FLAGS_BIT_NONEXCLUSIVE;
-       gpiod = devm_gpiod_get_optional(&client->dev,
-                                       "max8952,en",
-                                       gflags);
+       /*
+        * Do not use devm* here: the regulator core takes over the
+        * lifecycle management of the GPIO descriptor.
+        */
+       gpiod = gpiod_get_optional(&client->dev,
+                                  "max8952,en",
+                                  gflags);
        if (IS_ERR(gpiod))
                return PTR_ERR(gpiod);
        if (gpiod)
index e7a58b5..9aee144 100644 (file)
@@ -808,7 +808,13 @@ static int max8973_probe(struct i2c_client *client,
        config.of_node = client->dev.of_node;
        config.regmap = max->regmap;
 
-       /* Register the regulators */
+       /*
+        * Register the regulators
+        * Turn the GPIO descriptor over to the regulator core for
+        * lifecycle management if we pass an ena_gpiod.
+        */
+       if (config.ena_gpiod)
+               devm_gpiod_unhinge(&client->dev, config.ena_gpiod);
        rdev = devm_regulator_register(&client->dev, &max->desc, &config);
        if (IS_ERR(rdev)) {
                ret = PTR_ERR(rdev);
index 3bf5ddf..4d24872 100644 (file)
@@ -925,7 +925,7 @@ static int max8997_pmic_dt_parse_pdata(struct platform_device *pdev,
        pdata->regulators = rdata;
        for_each_child_of_node(regulators_np, reg_np) {
                for (i = 0; i < ARRAY_SIZE(regulators); i++)
-                       if (!of_node_cmp(reg_np->name, regulators[i].name))
+                       if (of_node_name_eq(reg_np, regulators[i].name))
                                break;
 
                if (i == ARRAY_SIZE(regulators)) {
index 65eb1e0..2243138 100644 (file)
@@ -186,7 +186,7 @@ struct mc13xxx_regulator_init_data *mc13xxx_parse_regulators_dt(
                for (i = 0; i < num_regulators; i++) {
                        if (!regulators[i].desc.name)
                                continue;
-                       if (!of_node_cmp(child->name,
+                       if (of_node_name_eq(child,
                                         regulators[i].desc.name)) {
                                p->id = i;
                                p->init_data = of_get_regulator_init_data(
diff --git a/drivers/regulator/mcp16502.c b/drivers/regulator/mcp16502.c
new file mode 100644 (file)
index 0000000..3479ae0
--- /dev/null
@@ -0,0 +1,552 @@
+// SPDX-License-Identifier: GPL-2.0
+//
+// MCP16502 PMIC driver
+//
+// Copyright (C) 2018 Microchip Technology Inc. and its subsidiaries
+//
+// Author: Andrei Stefanescu <andrei.stefanescu@microchip.com>
+//
+// Inspired from tps65086-regulator.c
+
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/suspend.h>
+
+#define VDD_LOW_SEL 0x0D
+#define VDD_HIGH_SEL 0x3F
+
+#define MCP16502_FLT BIT(7)
+#define MCP16502_ENS BIT(0)
+
+/*
+ * The PMIC has four sets of registers corresponding to four power modes:
+ * Performance, Active, Low-power, Hibernate.
+ *
+ * Registers:
+ * Each regulator has a register for each power mode. To access a register
+ * for a specific regulator and mode BASE_* and OFFSET_* need to be added.
+ *
+ * Operating modes:
+ * In order for the PMIC to transition to operating modes it has to be
+ * controlled via GPIO lines called LPM and HPM.
+ *
+ * The registers are fully configurable such that you can put all regulators in
+ * a low-power state while the PMIC is in Active mode. They are supposed to be
+ * configured at startup and then simply transition to/from a global low-power
+ * state by setting the GPIO lpm pin high/low.
+ *
+ * This driver keeps the PMIC in Active mode, Low-power state is set for the
+ * regulators by enabling/disabling operating mode (FPWM or Auto PFM).
+ *
+ * The PMIC's Low-power and Hibernate modes are used during standby/suspend.
+ * To enter standby/suspend the PMIC will go to Low-power mode. From there, it
+ * will transition to Hibernate when the PWRHLD line is set to low by the MPU.
+ */
+
+/*
+ * This function is useful for iterating over all regulators and accessing their
+ * registers in a generic way or accessing a regulator device by its id.
+ */
+#define MCP16502_BASE(i) (((i) + 1) << 4)
+#define MCP16502_STAT_BASE(i) ((i) + 5)
+
+#define MCP16502_OFFSET_MODE_A 0
+#define MCP16502_OFFSET_MODE_LPM 1
+#define MCP16502_OFFSET_MODE_HIB 2
+
+#define MCP16502_OPMODE_ACTIVE REGULATOR_MODE_NORMAL
+#define MCP16502_OPMODE_LPM REGULATOR_MODE_IDLE
+#define MCP16502_OPMODE_HIB REGULATOR_MODE_STANDBY
+
+#define MCP16502_MODE_AUTO_PFM 0
+#define MCP16502_MODE_FPWM BIT(6)
+
+#define MCP16502_VSEL 0x3F
+#define MCP16502_EN BIT(7)
+#define MCP16502_MODE BIT(6)
+
+#define MCP16502_MIN_REG 0x0
+#define MCP16502_MAX_REG 0x65
+
+static unsigned int mcp16502_of_map_mode(unsigned int mode)
+{
+       if (mode == REGULATOR_MODE_NORMAL || mode == REGULATOR_MODE_IDLE)
+               return mode;
+
+       return REGULATOR_MODE_INVALID;
+}
+
+#define MCP16502_REGULATOR(_name, _id, _ranges, _ops)                  \
+       [_id] = {                                                       \
+               .name                   = _name,                        \
+               .regulators_node        = of_match_ptr("regulators"),   \
+               .id                     = _id,                          \
+               .ops                    = &(_ops),                      \
+               .type                   = REGULATOR_VOLTAGE,            \
+               .owner                  = THIS_MODULE,                  \
+               .n_voltages             = MCP16502_VSEL + 1,            \
+               .linear_ranges          = _ranges,                      \
+               .n_linear_ranges        = ARRAY_SIZE(_ranges),          \
+               .of_match               = of_match_ptr(_name),          \
+               .of_map_mode            = mcp16502_of_map_mode,         \
+               .vsel_reg               = (((_id) + 1) << 4),           \
+               .vsel_mask              = MCP16502_VSEL,                \
+               .enable_reg             = (((_id) + 1) << 4),           \
+               .enable_mask            = MCP16502_EN,                  \
+       }
+
+enum {
+       BUCK1 = 0,
+       BUCK2,
+       BUCK3,
+       BUCK4,
+       LDO1,
+       LDO2,
+       NUM_REGULATORS
+};
+
+/*
+ * struct mcp16502 - PMIC representation
+ * @rdev: the regulators belonging to this chip
+ * @rmap: regmap to be used for I2C communication
+ * @lpm: LPM GPIO descriptor
+ */
+struct mcp16502 {
+       struct regulator_dev *rdev[NUM_REGULATORS];
+       struct regmap *rmap;
+       struct gpio_desc *lpm;
+};
+
+/*
+ * mcp16502_gpio_set_mode() - set the GPIO corresponding value
+ *
+ * Used to prepare transitioning into hibernate or resuming from it.
+ */
+static void mcp16502_gpio_set_mode(struct mcp16502 *mcp, int mode)
+{
+       switch (mode) {
+       case MCP16502_OPMODE_ACTIVE:
+               gpiod_set_value(mcp->lpm, 0);
+               break;
+       case MCP16502_OPMODE_LPM:
+       case MCP16502_OPMODE_HIB:
+               gpiod_set_value(mcp->lpm, 1);
+               break;
+       default:
+               pr_err("%s: %d invalid\n", __func__, mode);
+       }
+}
+
+/*
+ * mcp16502_get_reg() - get the PMIC's configuration register for opmode
+ *
+ * @rdev: the regulator whose register we are searching
+ * @opmode: the PMIC's operating mode ACTIVE, Low-power, Hibernate
+ */
+static int mcp16502_get_reg(struct regulator_dev *rdev, int opmode)
+{
+       int reg = MCP16502_BASE(rdev_get_id(rdev));
+
+       switch (opmode) {
+       case MCP16502_OPMODE_ACTIVE:
+               return reg + MCP16502_OFFSET_MODE_A;
+       case MCP16502_OPMODE_LPM:
+               return reg + MCP16502_OFFSET_MODE_LPM;
+       case MCP16502_OPMODE_HIB:
+               return reg + MCP16502_OFFSET_MODE_HIB;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * mcp16502_get_mode() - return the current operating mode of a regulator
+ *
+ * Note: all functions that are not part of entering/exiting standby/suspend
+ *      use the Active mode registers.
+ *
+ * Note: this is different from the PMIC's operatig mode, it is the
+ *      MODE bit from the regulator's register.
+ */
+static unsigned int mcp16502_get_mode(struct regulator_dev *rdev)
+{
+       unsigned int val;
+       int ret, reg;
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+
+       reg = mcp16502_get_reg(rdev, MCP16502_OPMODE_ACTIVE);
+       if (reg < 0)
+               return reg;
+
+       ret = regmap_read(mcp->rmap, reg, &val);
+       if (ret)
+               return ret;
+
+       switch (val & MCP16502_MODE) {
+       case MCP16502_MODE_FPWM:
+               return REGULATOR_MODE_NORMAL;
+       case MCP16502_MODE_AUTO_PFM:
+               return REGULATOR_MODE_IDLE;
+       default:
+               return REGULATOR_MODE_INVALID;
+       }
+}
+
+/*
+ * _mcp16502_set_mode() - helper for set_mode and set_suspend_mode
+ *
+ * @rdev: the regulator for which we are setting the mode
+ * @mode: the regulator's mode (the one from MODE bit)
+ * @opmode: the PMIC's operating mode: Active/Low-power/Hibernate
+ */
+static int _mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode,
+                             unsigned int op_mode)
+{
+       int val;
+       int reg;
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+
+       reg = mcp16502_get_reg(rdev, op_mode);
+       if (reg < 0)
+               return reg;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               val = MCP16502_MODE_FPWM;
+               break;
+       case REGULATOR_MODE_IDLE:
+               val = MCP16502_MODE_AUTO_PFM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       reg = regmap_update_bits(mcp->rmap, reg, MCP16502_MODE, val);
+       return reg;
+}
+
+/*
+ * mcp16502_set_mode() - regulator_ops set_mode
+ */
+static int mcp16502_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_ACTIVE);
+}
+
+/*
+ * mcp16502_get_status() - regulator_ops get_status
+ */
+static int mcp16502_get_status(struct regulator_dev *rdev)
+{
+       int ret;
+       unsigned int val;
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+
+       ret = regmap_read(mcp->rmap, MCP16502_STAT_BASE(rdev_get_id(rdev)),
+                         &val);
+       if (ret)
+               return ret;
+
+       if (val & MCP16502_FLT)
+               return REGULATOR_STATUS_ERROR;
+       else if (val & MCP16502_ENS)
+               return REGULATOR_STATUS_ON;
+       else if (!(val & MCP16502_ENS))
+               return REGULATOR_STATUS_OFF;
+
+       return REGULATOR_STATUS_UNDEFINED;
+}
+
+#ifdef CONFIG_SUSPEND
+/*
+ * mcp16502_suspend_get_target_reg() - get the reg of the target suspend PMIC
+ *                                    mode
+ */
+static int mcp16502_suspend_get_target_reg(struct regulator_dev *rdev)
+{
+       switch (pm_suspend_target_state) {
+       case PM_SUSPEND_STANDBY:
+               return mcp16502_get_reg(rdev, MCP16502_OPMODE_LPM);
+       case PM_SUSPEND_ON:
+       case PM_SUSPEND_MEM:
+               return mcp16502_get_reg(rdev, MCP16502_OPMODE_HIB);
+       default:
+               dev_err(&rdev->dev, "invalid suspend target: %d\n",
+                       pm_suspend_target_state);
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * mcp16502_set_suspend_voltage() - regulator_ops set_suspend_voltage
+ */
+static int mcp16502_set_suspend_voltage(struct regulator_dev *rdev, int uV)
+{
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+       int sel = regulator_map_voltage_linear_range(rdev, uV, uV);
+       int reg = mcp16502_suspend_get_target_reg(rdev);
+
+       if (sel < 0)
+               return sel;
+
+       if (reg < 0)
+               return reg;
+
+       return regmap_update_bits(mcp->rmap, reg, MCP16502_VSEL, sel);
+}
+
+/*
+ * mcp16502_set_suspend_mode() - regulator_ops set_suspend_mode
+ */
+static int mcp16502_set_suspend_mode(struct regulator_dev *rdev,
+                                    unsigned int mode)
+{
+       switch (pm_suspend_target_state) {
+       case PM_SUSPEND_STANDBY:
+               return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_LPM);
+       case PM_SUSPEND_ON:
+       case PM_SUSPEND_MEM:
+               return _mcp16502_set_mode(rdev, mode, MCP16502_OPMODE_HIB);
+       default:
+               dev_err(&rdev->dev, "invalid suspend target: %d\n",
+                       pm_suspend_target_state);
+       }
+
+       return -EINVAL;
+}
+
+/*
+ * mcp16502_set_suspend_enable() - regulator_ops set_suspend_enable
+ */
+static int mcp16502_set_suspend_enable(struct regulator_dev *rdev)
+{
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+       int reg = mcp16502_suspend_get_target_reg(rdev);
+
+       if (reg < 0)
+               return reg;
+
+       return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, MCP16502_EN);
+}
+
+/*
+ * mcp16502_set_suspend_disable() - regulator_ops set_suspend_disable
+ */
+static int mcp16502_set_suspend_disable(struct regulator_dev *rdev)
+{
+       struct mcp16502 *mcp = rdev_get_drvdata(rdev);
+       int reg = mcp16502_suspend_get_target_reg(rdev);
+
+       if (reg < 0)
+               return reg;
+
+       return regmap_update_bits(mcp->rmap, reg, MCP16502_EN, 0);
+}
+#endif /* CONFIG_SUSPEND */
+
+static const struct regulator_ops mcp16502_buck_ops = {
+       .list_voltage                   = regulator_list_voltage_linear_range,
+       .map_voltage                    = regulator_map_voltage_linear_range,
+       .get_voltage_sel                = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel                = regulator_set_voltage_sel_regmap,
+       .enable                         = regulator_enable_regmap,
+       .disable                        = regulator_disable_regmap,
+       .is_enabled                     = regulator_is_enabled_regmap,
+       .get_status                     = mcp16502_get_status,
+
+       .set_mode                       = mcp16502_set_mode,
+       .get_mode                       = mcp16502_get_mode,
+
+#ifdef CONFIG_SUSPEND
+       .set_suspend_voltage            = mcp16502_set_suspend_voltage,
+       .set_suspend_mode               = mcp16502_set_suspend_mode,
+       .set_suspend_enable             = mcp16502_set_suspend_enable,
+       .set_suspend_disable            = mcp16502_set_suspend_disable,
+#endif /* CONFIG_SUSPEND */
+};
+
+/*
+ * LDOs cannot change operating modes.
+ */
+static const struct regulator_ops mcp16502_ldo_ops = {
+       .list_voltage                   = regulator_list_voltage_linear_range,
+       .map_voltage                    = regulator_map_voltage_linear_range,
+       .get_voltage_sel                = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel                = regulator_set_voltage_sel_regmap,
+       .enable                         = regulator_enable_regmap,
+       .disable                        = regulator_disable_regmap,
+       .is_enabled                     = regulator_is_enabled_regmap,
+       .get_status                     = mcp16502_get_status,
+
+#ifdef CONFIG_SUSPEND
+       .set_suspend_voltage            = mcp16502_set_suspend_voltage,
+       .set_suspend_enable             = mcp16502_set_suspend_enable,
+       .set_suspend_disable            = mcp16502_set_suspend_disable,
+#endif /* CONFIG_SUSPEND */
+};
+
+static const struct of_device_id mcp16502_ids[] = {
+       { .compatible = "microchip,mcp16502", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, mcp16502_ids);
+
+static const struct regulator_linear_range b1l12_ranges[] = {
+       REGULATOR_LINEAR_RANGE(1200000, VDD_LOW_SEL, VDD_HIGH_SEL, 50000),
+};
+
+static const struct regulator_linear_range b234_ranges[] = {
+       REGULATOR_LINEAR_RANGE(600000, VDD_LOW_SEL, VDD_HIGH_SEL, 25000),
+};
+
+static const struct regulator_desc mcp16502_desc[] = {
+       /* MCP16502_REGULATOR(_name, _id, ranges, regulator_ops) */
+       MCP16502_REGULATOR("VDD_IO", BUCK1, b1l12_ranges, mcp16502_buck_ops),
+       MCP16502_REGULATOR("VDD_DDR", BUCK2, b234_ranges, mcp16502_buck_ops),
+       MCP16502_REGULATOR("VDD_CORE", BUCK3, b234_ranges, mcp16502_buck_ops),
+       MCP16502_REGULATOR("VDD_OTHER", BUCK4, b234_ranges, mcp16502_buck_ops),
+       MCP16502_REGULATOR("LDO1", LDO1, b1l12_ranges, mcp16502_ldo_ops),
+       MCP16502_REGULATOR("LDO2", LDO2, b1l12_ranges, mcp16502_ldo_ops)
+};
+
+static const struct regmap_range mcp16502_ranges[] = {
+       regmap_reg_range(MCP16502_MIN_REG, MCP16502_MAX_REG)
+};
+
+static const struct regmap_access_table mcp16502_yes_reg_table = {
+       .yes_ranges = mcp16502_ranges,
+       .n_yes_ranges = ARRAY_SIZE(mcp16502_ranges),
+};
+
+static const struct regmap_config mcp16502_regmap_config = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = MCP16502_MAX_REG,
+       .cache_type     = REGCACHE_NONE,
+       .rd_table       = &mcp16502_yes_reg_table,
+       .wr_table       = &mcp16502_yes_reg_table,
+};
+
+/*
+ * set_up_regulators() - initialize all regulators
+ */
+static int setup_regulators(struct mcp16502 *mcp, struct device *dev,
+                           struct regulator_config config)
+{
+       int i;
+
+       for (i = 0; i < NUM_REGULATORS; i++) {
+               mcp->rdev[i] = devm_regulator_register(dev,
+                                                      &mcp16502_desc[i],
+                                                      &config);
+               if (IS_ERR(mcp->rdev[i])) {
+                       dev_err(dev,
+                               "failed to register %s regulator %ld\n",
+                               mcp16502_desc[i].name, PTR_ERR(mcp->rdev[i]));
+                       return PTR_ERR(mcp->rdev[i]);
+               }
+       }
+
+       return 0;
+}
+
+static int mcp16502_probe(struct i2c_client *client,
+                         const struct i2c_device_id *id)
+{
+       struct regulator_config config = { };
+       struct device *dev;
+       struct mcp16502 *mcp;
+       int ret = 0;
+
+       dev = &client->dev;
+       config.dev = dev;
+
+       mcp = devm_kzalloc(dev, sizeof(*mcp), GFP_KERNEL);
+       if (!mcp)
+               return -ENOMEM;
+
+       mcp->rmap = devm_regmap_init_i2c(client, &mcp16502_regmap_config);
+       if (IS_ERR(mcp->rmap)) {
+               ret = PTR_ERR(mcp->rmap);
+               dev_err(dev, "regmap init failed: %d\n", ret);
+               return ret;
+       }
+
+       i2c_set_clientdata(client, mcp);
+       config.regmap = mcp->rmap;
+       config.driver_data = mcp;
+
+       mcp->lpm = devm_gpiod_get(dev, "lpm", GPIOD_OUT_LOW);
+       if (IS_ERR(mcp->lpm)) {
+               dev_err(dev, "failed to get lpm pin: %ld\n", PTR_ERR(mcp->lpm));
+               return PTR_ERR(mcp->lpm);
+       }
+
+       ret = setup_regulators(mcp, dev, config);
+       if (ret != 0)
+               return ret;
+
+       mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int mcp16502_suspend_noirq(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mcp16502 *mcp = i2c_get_clientdata(client);
+
+       mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_LPM);
+
+       return 0;
+}
+
+static int mcp16502_resume_noirq(struct device *dev)
+{
+       struct i2c_client *client = to_i2c_client(dev);
+       struct mcp16502 *mcp = i2c_get_clientdata(client);
+
+       mcp16502_gpio_set_mode(mcp, MCP16502_OPMODE_ACTIVE);
+
+       return 0;
+}
+#endif
+
+#ifdef CONFIG_PM
+static const struct dev_pm_ops mcp16502_pm_ops = {
+       SET_NOIRQ_SYSTEM_SLEEP_PM_OPS(mcp16502_suspend_noirq,
+                                     mcp16502_resume_noirq)
+};
+#endif
+static const struct i2c_device_id mcp16502_i2c_id[] = {
+       { "mcp16502", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, mcp16502_i2c_id);
+
+static struct i2c_driver mcp16502_drv = {
+       .probe          = mcp16502_probe,
+       .driver         = {
+               .name   = "mcp16502-regulator",
+               .of_match_table = of_match_ptr(mcp16502_ids),
+#ifdef CONFIG_PM
+               .pm = &mcp16502_pm_ops,
+#endif
+       },
+       .id_table       = mcp16502_i2c_id,
+};
+
+module_i2c_driver(mcp16502_drv);
+
+MODULE_VERSION("1.0");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MCP16502 PMIC driver");
+MODULE_AUTHOR("Andrei Stefanescu andrei.stefanescu@microchip.com");
index c4223b3..ffa5fc3 100644 (file)
@@ -20,6 +20,7 @@
 #include "internal.h"
 
 static const char *const regulator_states[PM_SUSPEND_MAX + 1] = {
+       [PM_SUSPEND_STANDBY]    = "regulator-state-standby",
        [PM_SUSPEND_MEM]        = "regulator-state-mem",
        [PM_SUSPEND_MAX]        = "regulator-state-disk",
 };
@@ -170,6 +171,10 @@ static void of_get_regulation_constraints(struct device_node *np,
                                  &pval))
                constraints->max_spread = pval;
 
+       if (!of_property_read_u32(np, "regulator-max-step-microvolt",
+                                 &pval))
+               constraints->max_uV_step = pval;
+
        constraints->over_current_protection = of_property_read_bool(np,
                                        "regulator-over-current-protection");
 
@@ -181,9 +186,11 @@ static void of_get_regulation_constraints(struct device_node *np,
                case PM_SUSPEND_MAX:
                        suspend_state = &constraints->state_disk;
                        break;
+               case PM_SUSPEND_STANDBY:
+                       suspend_state = &constraints->state_standby;
+                       break;
                case PM_SUSPEND_ON:
                case PM_SUSPEND_TO_IDLE:
-               case PM_SUSPEND_STANDBY:
                default:
                        continue;
                }
@@ -364,24 +371,25 @@ int of_regulator_match(struct device *dev, struct device_node *node,
 }
 EXPORT_SYMBOL_GPL(of_regulator_match);
 
-struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
-                                           const struct regulator_desc *desc,
-                                           struct regulator_config *config,
-                                           struct device_node **node)
+struct device_node *regulator_of_get_init_node(struct device *dev,
+                                              const struct regulator_desc *desc)
 {
        struct device_node *search, *child;
-       struct regulator_init_data *init_data = NULL;
        const char *name;
 
        if (!dev->of_node || !desc->of_match)
                return NULL;
 
-       if (desc->regulators_node)
+       if (desc->regulators_node) {
                search = of_get_child_by_name(dev->of_node,
                                              desc->regulators_node);
-       else
+       } else {
                search = of_node_get(dev->of_node);
 
+               if (!strcmp(desc->of_match, search->name))
+                       return search;
+       }
+
        if (!search) {
                dev_dbg(dev, "Failed to find regulator container node '%s'\n",
                        desc->regulators_node);
@@ -393,35 +401,48 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
                if (!name)
                        name = child->name;
 
-               if (strcmp(desc->of_match, name))
-                       continue;
+               if (!strcmp(desc->of_match, name))
+                       return of_node_get(child);
+       }
 
-               init_data = of_get_regulator_init_data(dev, child, desc);
-               if (!init_data) {
-                       dev_err(dev,
-                               "failed to parse DT for regulator %pOFn\n",
-                               child);
-                       break;
-               }
+       of_node_put(search);
 
-               if (desc->of_parse_cb) {
-                       if (desc->of_parse_cb(child, desc, config)) {
-                               dev_err(dev,
-                                       "driver callback failed to parse DT for regulator %pOFn\n",
-                                       child);
-                               init_data = NULL;
-                               break;
-                       }
-               }
+       return NULL;
+}
+
+struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
+                                           const struct regulator_desc *desc,
+                                           struct regulator_config *config,
+                                           struct device_node **node)
+{
+       struct device_node *child;
+       struct regulator_init_data *init_data = NULL;
 
-               of_node_get(child);
-               *node = child;
-               break;
+       child = regulator_of_get_init_node(dev, desc);
+       if (!child)
+               return NULL;
+
+       init_data = of_get_regulator_init_data(dev, child, desc);
+       if (!init_data) {
+               dev_err(dev, "failed to parse DT for regulator %pOFn\n", child);
+               goto error;
        }
 
-       of_node_put(search);
+       if (desc->of_parse_cb && desc->of_parse_cb(child, desc, config)) {
+               dev_err(dev,
+                       "driver callback failed to parse DT for regulator %pOFn\n",
+                       child);
+               goto error;
+       }
+
+       *node = child;
 
        return init_data;
+
+error:
+       of_node_put(child);
+
+       return NULL;
 }
 
 static int of_node_match(struct device *dev, const void *data)
index bb5ab7d..c2cc392 100644 (file)
@@ -443,13 +443,16 @@ static int palmas_ldo_write(struct palmas *palmas, unsigned int reg,
 static int palmas_set_mode_smps(struct regulator_dev *dev, unsigned int mode)
 {
        int id = rdev_get_id(dev);
+       int ret;
        struct palmas_pmic *pmic = rdev_get_drvdata(dev);
        struct palmas_pmic_driver_data *ddata = pmic->palmas->pmic_ddata;
        struct palmas_regs_info *rinfo = &ddata->palmas_regs_info[id];
        unsigned int reg;
        bool rail_enable = true;
 
-       palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, &reg);
+       ret = palmas_smps_read(pmic->palmas, rinfo->ctrl_addr, &reg);
+       if (ret)
+               return ret;
 
        reg &= ~PALMAS_SMPS12_CTRL_MODE_ACTIVE_MASK;
 
index dd41a9b..df5df1c 100644 (file)
@@ -370,6 +370,7 @@ static struct pfuze_regulator pfuze100_regulators[] = {
        PFUZE100_VGEN_REG(PFUZE100, VGEN4, PFUZE100_VGEN4VOL, 1800000, 3300000, 100000),
        PFUZE100_VGEN_REG(PFUZE100, VGEN5, PFUZE100_VGEN5VOL, 1800000, 3300000, 100000),
        PFUZE100_VGEN_REG(PFUZE100, VGEN6, PFUZE100_VGEN6VOL, 1800000, 3300000, 100000),
+       PFUZE100_COIN_REG(PFUZE100, COIN, PFUZE100_COINVOL, 0x7, pfuze100_coin),
 };
 
 static struct pfuze_regulator pfuze200_regulators[] = {
@@ -436,6 +437,7 @@ static struct of_regulator_match pfuze100_matches[] = {
        { .name = "vgen4",      },
        { .name = "vgen5",      },
        { .name = "vgen6",      },
+       { .name = "coin",       },
 };
 
 /* PFUZE200 */
index 39ccf53..b2c2d01 100644 (file)
@@ -410,7 +410,7 @@ static int rpmh_regulator_init_vreg(struct rpmh_vreg *vreg, struct device *dev,
        vreg->dev = dev;
 
        for (rpmh_data = pmic_rpmh_data; rpmh_data->name; rpmh_data++)
-               if (!strcmp(rpmh_data->name, node->name))
+               if (of_node_name_eq(node, rpmh_data->name))
                        break;
 
        if (!rpmh_data->name) {
index 5bb6f4c..ee4a23a 100644 (file)
@@ -5,7 +5,7 @@
 
 #include <linux/bug.h>
 #include <linux/err.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/of.h>
@@ -14,7 +14,6 @@
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/of_regulator.h>
-#include <linux/of_gpio.h>
 #include <linux/mfd/samsung/core.h>
 #include <linux/mfd/samsung/s2mps11.h>
 #include <linux/mfd/samsung/s2mps13.h>
@@ -44,7 +43,7 @@ struct s2mps11_info {
         * Array (size: number of regulators) with GPIO-s for external
         * sleep control.
         */
-       int *ext_control_gpio;
+       struct gpio_desc **ext_control_gpiod;
 };
 
 static int get_ramp_delay(int ramp_delay)
@@ -511,7 +510,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
        case S2MPS14X:
                if (test_bit(rdev_get_id(rdev), s2mps11->suspend_state))
                        val = S2MPS14_ENABLE_SUSPEND;
-               else if (gpio_is_valid(s2mps11->ext_control_gpio[rdev_get_id(rdev)]))
+               else if (s2mps11->ext_control_gpiod[rdev_get_id(rdev)])
                        val = S2MPS14_ENABLE_EXT_CONTROL;
                else
                        val = rdev->desc->enable_mask;
@@ -805,7 +804,7 @@ static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11,
 static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev,
                struct of_regulator_match *rdata, struct s2mps11_info *s2mps11)
 {
-       int *gpio = s2mps11->ext_control_gpio;
+       struct gpio_desc **gpio = s2mps11->ext_control_gpiod;
        unsigned int i;
        unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11,
                S2MPS14_LDO12 };
@@ -816,11 +815,20 @@ static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev,
                if (!rdata[reg].init_data || !rdata[reg].of_node)
                        continue;
 
-               gpio[reg] = of_get_named_gpio(rdata[reg].of_node,
-                               "samsung,ext-control-gpios", 0);
-               if (gpio_is_valid(gpio[reg]))
-                       dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n",
-                                       gpio[reg], reg, rdata[reg].name);
+               gpio[reg] = devm_gpiod_get_from_of_node(&pdev->dev,
+                               rdata[reg].of_node,
+                               "samsung,ext-control-gpios",
+                               0,
+                               GPIOD_OUT_HIGH | GPIOD_FLAGS_BIT_NONEXCLUSIVE,
+                               "s2mps11-regulator");
+               if (IS_ERR(gpio[reg])) {
+                       dev_err(&pdev->dev, "Failed to get control GPIO for %d/%s\n",
+                               reg, rdata[reg].name);
+                       continue;
+               }
+               if (gpio[reg])
+                       dev_dbg(&pdev->dev, "Using GPIO for ext-control over %d/%s\n",
+                               reg, rdata[reg].name);
        }
 }
 
@@ -1126,17 +1134,10 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       s2mps11->ext_control_gpio = devm_kmalloc_array(&pdev->dev,
-                       rdev_num, sizeof(*s2mps11->ext_control_gpio),
-                       GFP_KERNEL);
-       if (!s2mps11->ext_control_gpio)
+       s2mps11->ext_control_gpiod = devm_kcalloc(&pdev->dev, rdev_num,
+                              sizeof(*s2mps11->ext_control_gpiod), GFP_KERNEL);
+       if (!s2mps11->ext_control_gpiod)
                return -ENOMEM;
-       /*
-        * 0 is a valid GPIO so initialize all GPIO-s to negative value
-        * to indicate that external control won't be used for this regulator.
-        */
-       for (i = 0; i < rdev_num; i++)
-               s2mps11->ext_control_gpio[i] = -EINVAL;
 
        if (!iodev->dev->of_node) {
                if (iodev->pdata) {
@@ -1166,8 +1167,6 @@ common_reg:
        config.dev = &pdev->dev;
        config.regmap = iodev->regmap_pmic;
        config.driver_data = s2mps11;
-       config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
-       config.ena_gpio_initialized = true;
        for (i = 0; i < rdev_num; i++) {
                struct regulator_dev *regulator;
 
@@ -1178,8 +1177,13 @@ common_reg:
                        config.init_data = rdata[i].init_data;
                        config.of_node = rdata[i].of_node;
                }
-               config.ena_gpio = s2mps11->ext_control_gpio[i];
-
+               config.ena_gpiod = s2mps11->ext_control_gpiod[i];
+               /*
+                * Hand the GPIO descriptor management over to the regulator
+                * core, remove it from devres management.
+                */
+               if (config.ena_gpiod)
+                       devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod);
                regulator = devm_regulator_register(&pdev->dev,
                                                &regulators[i], &config);
                if (IS_ERR(regulator)) {
@@ -1189,7 +1193,7 @@ common_reg:
                        goto out;
                }
 
-               if (gpio_is_valid(s2mps11->ext_control_gpio[i])) {
+               if (s2mps11->ext_control_gpiod[i]) {
                        ret = s2mps14_pmic_enable_ext_control(s2mps11,
                                        regulator);
                        if (ret < 0) {
index 219b9af..b581f01 100644 (file)
@@ -561,7 +561,7 @@ static int s5m8767_pmic_dt_parse_pdata(struct platform_device *pdev,
        pdata->opmode = rmode;
        for_each_child_of_node(regulators_np, reg_np) {
                for (i = 0; i < ARRAY_SIZE(regulators); i++)
-                       if (!of_node_cmp(reg_np->name, regulators[i].name))
+                       if (of_node_name_eq(reg_np, regulators[i].name))
                                break;
 
                if (i == ARRAY_SIZE(regulators)) {
@@ -956,10 +956,17 @@ static int s5m8767_pmic_probe(struct platform_device *pdev)
                config.regmap = iodev->regmap_pmic;
                config.of_node = pdata->regulators[i].reg_node;
                config.ena_gpiod = NULL;
-               if (pdata->regulators[i].ext_control_gpiod)
+               if (pdata->regulators[i].ext_control_gpiod) {
+                       /* Assigns config.ena_gpiod */
                        s5m8767_regulator_config_ext_control(s5m8767,
                                        &pdata->regulators[i], &config);
 
+                       /*
+                        * Hand the GPIO descriptor management over to the
+                        * regulator core, remove it from devres management.
+                        */
+                       devm_gpiod_unhinge(s5m8767->dev, config.ena_gpiod);
+               }
                rdev = devm_regulator_register(&pdev->dev, &regulators[id],
                                                  &config);
                if (IS_ERR(rdev)) {
index e15634e..16ba029 100644 (file)
@@ -489,14 +489,14 @@ static irqreturn_t stpmic1_curlim_irq_handler(int irq, void *data)
 {
        struct regulator_dev *rdev = (struct regulator_dev *)data;
 
-       mutex_lock(&rdev->mutex);
+       regulator_lock(rdev);
 
        /* Send an overcurrent notification */
        regulator_notifier_call_chain(rdev,
                                      REGULATOR_EVENT_OVER_CURRENT,
                                      NULL);
 
-       mutex_unlock(&rdev->mutex);
+       regulator_unlock(rdev);
 
        return IRQ_HANDLED;
 }
index db714d5..0614551 100644 (file)
@@ -480,6 +480,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev)
                else
                        config.of_node = NULL;
 
+               /*
+                * Hand the GPIO descriptor management over to the regulator
+                * core, remove it from devres management.
+                */
+               if (config.ena_gpiod)
+                       devm_gpiod_unhinge(&pdev->dev, config.ena_gpiod);
                rdev = devm_regulator_register(&pdev->dev, ri->desc, &config);
                if (IS_ERR(rdev)) {
                        dev_err(&pdev->dev, "failed to register regulator %s\n",
index 02ccdaa..5ebb6ee 100644 (file)
@@ -1102,8 +1102,10 @@ static int tps65910_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, pmic);
 
        /* Give control of all register to control port */
-       tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
+       err = tps65910_reg_set_bits(pmic->mfd, TPS65910_DEVCTRL,
                                DEVCTRL_SR_CTL_I2C_SEL_MASK);
+       if (err < 0)
+               return err;
 
        switch (tps65910_chip_id(tps65910)) {
        case TPS65910:
index 8ad11b0..a1c7dfe 100644 (file)
@@ -1153,7 +1153,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data)
 {
        struct regulator_dev *rdev = (struct regulator_dev *)data;
 
-       mutex_lock(&rdev->mutex);
+       regulator_lock(rdev);
        if (irq == WM8350_IRQ_CS1 || irq == WM8350_IRQ_CS2)
                regulator_notifier_call_chain(rdev,
                                              REGULATOR_EVENT_REGULATION_OUT,
@@ -1162,7 +1162,7 @@ static irqreturn_t pmic_uv_handler(int irq, void *data)
                regulator_notifier_call_chain(rdev,
                                              REGULATOR_EVENT_UNDER_VOLTAGE,
                                              NULL);
-       mutex_unlock(&rdev->mutex);
+       regulator_unlock(rdev);
 
        return IRQ_HANDLED;
 }
index 7a4ce6d..38928cd 100644 (file)
@@ -19,7 +19,7 @@
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
-#include <linux/gpio.h>
+#include <linux/gpio/consumer.h>
 #include <linux/slab.h>
 
 #include <linux/mfd/wm8994/core.h>
@@ -129,6 +129,7 @@ static int wm8994_ldo_probe(struct platform_device *pdev)
        int id = pdev->id % ARRAY_SIZE(pdata->ldo);
        struct regulator_config config = { };
        struct wm8994_ldo *ldo;
+       struct gpio_desc *gpiod;
        int ret;
 
        dev_dbg(&pdev->dev, "Probing LDO%d\n", id + 1);
@@ -145,12 +146,18 @@ static int wm8994_ldo_probe(struct platform_device *pdev)
        config.driver_data = ldo;
        config.regmap = wm8994->regmap;
        config.init_data = &ldo->init_data;
-       if (pdata) {
-               config.ena_gpio = pdata->ldo[id].enable;
-       } else if (wm8994->dev->of_node) {
-               config.ena_gpio = wm8994->pdata.ldo[id].enable;
-               config.ena_gpio_initialized = true;
-       }
+
+       /*
+        * Look up LDO enable GPIO from the parent device node, we don't
+        * use devm because the regulator core will free the GPIO
+        */
+       gpiod = gpiod_get_optional(pdev->dev.parent,
+                                  id ? "wlf,ldo2ena" : "wlf,ldo1ena",
+                                  GPIOD_OUT_LOW |
+                                  GPIOD_FLAGS_BIT_NONEXCLUSIVE);
+       if (IS_ERR(gpiod))
+               return PTR_ERR(gpiod);
+       config.ena_gpiod = gpiod;
 
        /* Use default constraints if none set up */
        if (!pdata || !pdata->ldo[id].init_data || wm8994->dev->of_node) {
@@ -159,12 +166,17 @@ static int wm8994_ldo_probe(struct platform_device *pdev)
 
                ldo->init_data = wm8994_ldo_default[id];
                ldo->init_data.consumer_supplies = &ldo->supply;
-               if (!config.ena_gpio)
+               if (!gpiod)
                        ldo->init_data.constraints.valid_ops_mask = 0;
        } else {
                ldo->init_data = *pdata->ldo[id].init_data;
        }
 
+       /*
+        * At this point the GPIO descriptor is handled over to the
+        * regulator core and we need not worry about it on the
+        * error path.
+        */
        ldo->regulator = devm_regulator_register(&pdev->dev,
                                                 &wm8994_ldo_desc[id],
                                                 &config);
@@ -172,15 +184,12 @@ static int wm8994_ldo_probe(struct platform_device *pdev)
                ret = PTR_ERR(ldo->regulator);
                dev_err(wm8994->dev, "Failed to register LDO%d: %d\n",
                        id + 1, ret);
-               goto err;
+               return ret;
        }
 
        platform_set_drvdata(pdev, ldo);
 
        return 0;
-
-err:
-       return ret;
 }
 
 static struct platform_driver wm8994_ldo_driver = {
index b8163b4..e0f0611 100644 (file)
@@ -90,6 +90,9 @@
 #define PIO_DATAOUT_1B         0x0020
 #define PIO_DATAOUT_4B         0x0024
 
+#define RD_FIFO_CFG            0x0028
+#define CONTINUOUS_MODE                BIT(0)
+
 #define RD_FIFO_STATUS 0x002c
 #define FIFO_EMPTY     BIT(11)
 #define WR_CNTS_MSK    0x7f0
 #define RDY_16BYTE     BIT(1)
 #define FIFO_RDY       BIT(0)
 
-#define RD_FIFO_CFG            0x0028
-#define CONTINUOUS_MODE                BIT(0)
-
 #define RD_FIFO_RESET          0x0030
 #define RESET_FIFO             BIT(0)
 
@@ -139,7 +139,7 @@ struct qcom_qspi {
        struct device *dev;
        struct clk_bulk_data clks[QSPI_NUM_CLKS];
        struct qspi_xfer xfer;
-       /* Lock to protect data accessed by IRQs */
+       /* Lock to protect xfer and IRQ accessed registers */
        spinlock_t lock;
 };
 
diff --git a/include/dt-bindings/regulator/active-semi,8945a-regulator.h b/include/dt-bindings/regulator/active-semi,8945a-regulator.h
new file mode 100644 (file)
index 0000000..9bdba5e
--- /dev/null
@@ -0,0 +1,30 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * Copyright (c) 2018 Microchip Technology, Inc. All rights reserved.
+ *
+ * Device Tree binding constants for the ACT8945A PMIC regulators
+ */
+
+#ifndef _DT_BINDINGS_REGULATOR_ACT8945A_H
+#define _DT_BINDINGS_REGULATOR_ACT8945A_H
+
+/*
+ * These constants should be used to specify regulator modes in device tree for
+ * ACT8945A regulators as follows:
+ * ACT8945A_REGULATOR_MODE_FIXED:      It is specific to DCDC regulators and it
+ *                                     specifies the usage of fixed-frequency
+ *                                     PWM.
+ *
+ * ACT8945A_REGULATOR_MODE_NORMAL:     It is specific to LDO regulators and it
+ *                                     specifies the usage of normal mode.
+ *
+ * ACT8945A_REGULATOR_MODE_LOWPOWER:   For DCDC and LDO regulators; it specify
+ *                                     the usage of proprietary power-saving
+ *                                     mode.
+ */
+
+#define ACT8945A_REGULATOR_MODE_FIXED          1
+#define ACT8945A_REGULATOR_MODE_NORMAL         2
+#define ACT8945A_REGULATOR_MODE_LOWPOWER       3
+
+#endif
index f2f8877..8aebcf8 100644 (file)
@@ -104,6 +104,7 @@ struct gpio_descs *__must_check
 devm_gpiod_get_array_optional(struct device *dev, const char *con_id,
                              enum gpiod_flags flags);
 void devm_gpiod_put(struct device *dev, struct gpio_desc *desc);
+void devm_gpiod_unhinge(struct device *dev, struct gpio_desc *desc);
 void devm_gpiod_put_array(struct device *dev, struct gpio_descs *descs);
 
 int gpiod_get_direction(struct gpio_desc *desc);
@@ -172,6 +173,10 @@ int desc_to_gpio(const struct gpio_desc *desc);
 struct device_node;
 struct fwnode_handle;
 
+struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
+                                        const char *propname, int index,
+                                        enum gpiod_flags dflags,
+                                        const char *label);
 struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
                                              struct device_node *node,
                                              const char *propname, int index,
@@ -245,6 +250,15 @@ static inline void gpiod_put(struct gpio_desc *desc)
        WARN_ON(1);
 }
 
+static inline void devm_gpiod_unhinge(struct device *dev,
+                                     struct gpio_desc *desc)
+{
+       might_sleep();
+
+       /* GPIO can never have been requested */
+       WARN_ON(1);
+}
+
 static inline void gpiod_put_array(struct gpio_descs *descs)
 {
        might_sleep();
@@ -518,6 +532,15 @@ struct device_node;
 struct fwnode_handle;
 
 static inline
+struct gpio_desc *gpiod_get_from_of_node(struct device_node *node,
+                                        const char *propname, int index,
+                                        enum gpiod_flags dflags,
+                                        const char *label)
+{
+       return ERR_PTR(-ENOSYS);
+}
+
+static inline
 struct gpio_desc *devm_gpiod_get_from_of_node(struct device *dev,
                                              struct device_node *node,
                                              const char *propname, int index,
index 517e60e..1293695 100644 (file)
@@ -35,7 +35,7 @@ enum axp20x_variants {
 #define AXP152_ALDO_OP_MODE            0x13
 #define AXP152_LDO0_CTRL               0x15
 #define AXP152_DCDC2_V_OUT             0x23
-#define AXP152_DCDC2_V_SCAL            0x25
+#define AXP152_DCDC2_V_RAMP            0x25
 #define AXP152_DCDC1_V_OUT             0x26
 #define AXP152_DCDC3_V_OUT             0x27
 #define AXP152_ALDO12_V_OUT            0x28
@@ -53,7 +53,7 @@ enum axp20x_variants {
 #define AXP20X_USB_OTG_STATUS          0x02
 #define AXP20X_PWR_OUT_CTRL            0x12
 #define AXP20X_DCDC2_V_OUT             0x23
-#define AXP20X_DCDC2_LDO3_V_SCAL       0x25
+#define AXP20X_DCDC2_LDO3_V_RAMP       0x25
 #define AXP20X_DCDC3_V_OUT             0x27
 #define AXP20X_LDO24_V_OUT             0x28
 #define AXP20X_LDO3_V_OUT              0x29
index b19c370..f346167 100644 (file)
@@ -20,9 +20,6 @@
 #define WM8994_NUM_AIF   3
 
 struct wm8994_ldo_pdata {
-       /** GPIOs to enable regulator, 0 or less if not available */
-       int enable;
-
        const struct regulator_init_data *init_data;
 };
 
index 25602af..f3f7605 100644 (file)
@@ -508,7 +508,7 @@ static inline int regulator_get_error_flags(struct regulator *regulator,
 
 static inline int regulator_set_load(struct regulator *regulator, int load_uA)
 {
-       return REGULATOR_MODE_NORMAL;
+       return 0;
 }
 
 static inline int regulator_allow_bypass(struct regulator *regulator,
index a9c0301..389bcaf 100644 (file)
 #ifndef __LINUX_REGULATOR_DRIVER_H_
 #define __LINUX_REGULATOR_DRIVER_H_
 
-#define MAX_COUPLED            4
+#define MAX_COUPLED            2
 
 #include <linux/device.h>
 #include <linux/notifier.h>
 #include <linux/regulator/consumer.h>
+#include <linux/ww_mutex.h>
 
 struct gpio_desc;
 struct regmap;
@@ -462,7 +463,7 @@ struct regulator_dev {
        struct coupling_desc coupling_desc;
 
        struct blocking_notifier_head notifier;
-       struct mutex mutex; /* consumer lock */
+       struct ww_mutex mutex; /* consumer lock */
        struct task_struct *mutex_owner;
        int ref_cnt;
        struct module *owner;
@@ -473,7 +474,6 @@ struct regulator_dev {
        struct regmap *regmap;
 
        struct delayed_work disable_work;
-       int deferred_disables;
 
        void *reg_data;         /* regulator_dev data */
 
@@ -545,4 +545,7 @@ int regulator_set_active_discharge_regmap(struct regulator_dev *rdev,
                                          bool enable);
 void *regulator_get_init_drvdata(struct regulator_init_data *reg_init_data);
 
+void regulator_lock(struct regulator_dev *rdev);
+void regulator_unlock(struct regulator_dev *rdev);
+
 #endif
index a459a5e..1d34a70 100644 (file)
@@ -158,6 +158,9 @@ struct regulation_constraints {
        /* used for coupled regulators */
        int max_spread;
 
+       /* used for changing voltage in steps */
+       int max_uV_step;
+
        /* valid regulator operating modes for this machine */
        unsigned int valid_modes_mask;
 
index cb5aecd..331d7d9 100644 (file)
@@ -33,7 +33,8 @@
 #define PFUZE100_VGEN4         12
 #define PFUZE100_VGEN5         13
 #define PFUZE100_VGEN6         14
-#define PFUZE100_MAX_REGULATOR 15
+#define PFUZE100_COIN          15
+#define PFUZE100_MAX_REGULATOR 16
 
 #define PFUZE200_SW1AB         0
 #define PFUZE200_SW2           1