Merge tag 'regulator-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 00:30:20 +0000 (17:30 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 24 Jun 2015 00:30:20 +0000 (17:30 -0700)
Pull regulator updates from Mark Brown:
 "Another fairly quiet release, some new drivers with generic handling
  for minor features but nothing that makes a substantial difference
  outside of the subsystem or for most boards:

   - support for a bunch of new parameters which are present on enough
     regulators to be worth having generic handling for in the
     framework.

   - fixes for some issues with printing constraints during boot which
     should probably have gone in for v4.1 but didn't.

   - new drivers for Dialog DA9062, Maxim MAX77621 and Qualcomm SPMI
     regulators"

* tag 'regulator-v4.2' of git://git.kernel.org/pub/scm/linux/kernel/git/broonie/regulator: (45 commits)
  regulator: qcom_spmi: Fix calculating number of voltages
  regulator: qcom_spmi: Add missing braces for aligned code
  regulator: fix simple_return.cocci warnings
  regulator: Add QCOM SPMI regulator driver
  regulator: Add docbook for soft start
  regulator: Add input current limit support
  regulator: Add soft start support
  regulator: Add pull down support
  regulator: Add system_load constraint
  regulator: max8973: Fix up ramp_delay for MAX8973_RAMP_25mV_PER_US case
  regulator: core: replace sprintf with scnprintf
  regulator: core: fix constraints output buffer
  regulator: core: Don't corrupt display when printing uV offsets
  regulator: max8973: add support for MAX77621
  regulator: max8973: configure ramp delay through callback
  regulator: pwm-regulator: Diffientiate between dev (device) and rdev (regulator_dev)
  regulator: pwm-regulator: Remove superfluous is_enabled check
  regulator: pwm-regulator: Remove unnecessary descriptor attribute from ddata
  regulator: core: Don't spew backtraces on duplicate sysfs
  regulator: da9063: Fix up irq leak
  ...

28 files changed:
Documentation/devicetree/bindings/regulator/max8973-regulator.txt
Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt [new file with mode: 0644]
Documentation/devicetree/bindings/regulator/regulator.txt
drivers/regulator/88pm8607.c
drivers/regulator/Kconfig
drivers/regulator/Makefile
drivers/regulator/arizona-ldo1.c
drivers/regulator/core.c
drivers/regulator/da9062-regulator.c [new file with mode: 0644]
drivers/regulator/da9063-regulator.c
drivers/regulator/fan53555.c
drivers/regulator/helpers.c
drivers/regulator/lp8755.c
drivers/regulator/max14577.c
drivers/regulator/max77686.c
drivers/regulator/max77693.c
drivers/regulator/max77843.c
drivers/regulator/max8973-regulator.c
drivers/regulator/of_regulator.c
drivers/regulator/pwm-regulator.c
drivers/regulator/qcom_spmi-regulator.c [new file with mode: 0644]
drivers/regulator/s2mps11.c
drivers/regulator/wm831x-dcdc.c
drivers/regulator/wm831x-isink.c
drivers/regulator/wm831x-ldo.c
include/linux/regulator/driver.h
include/linux/regulator/machine.h
include/linux/regulator/max8973-regulator.h

index 4f15d8a1bfd005c09484d2acf76dc59f3a73107d..55efb24e5683c429776308e364c996b500f92134 100644 (file)
@@ -2,12 +2,30 @@
 
 Required properties:
 
-- compatible:  must be "maxim,max8973"
+- compatible:  must be one of following:
+                       "maxim,max8973"
+                       "maxim,max77621".
 - reg:         the i2c slave address of the regulator. It should be 0x1b.
 
 Any standard regulator properties can be used to configure the single max8973
 DCDC.
 
+Optional properties:
+
+-maxim,externally-enable: boolean, externally control the regulator output
+               enable/disable.
+-maxim,enable-gpio: GPIO for enable control. If the valid GPIO is provided
+               then externally enable control will be considered.
+-maxim,dvs-gpio: GPIO which is connected to DVS pin of device.
+-maxim,dvs-default-state: Default state of GPIO during initialisation.
+               1 for HIGH and 0 for LOW.
+-maxim,enable-remote-sense: boolean, enable reote sense.
+-maxim,enable-falling-slew-rate: boolean, enable falling slew rate.
+-maxim,enable-active-discharge: boolean: enable active discharge.
+-maxim,enable-frequency-shift: boolean, enable 9% frequency shift.
+-maxim,enable-bias-control: boolean, enable bias control. By enabling this
+               startup delay can be reduce to 20us from 220us.
+
 Example:
 
        max8973@1b {
diff --git a/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt b/Documentation/devicetree/bindings/regulator/qcom,spmi-regulator.txt
new file mode 100644 (file)
index 0000000..75b4604
--- /dev/null
@@ -0,0 +1,121 @@
+Qualcomm SPMI Regulators
+
+- compatible:
+       Usage: required
+       Value type: <string>
+       Definition: must be one of:
+                       "qcom,pm8841-regulators"
+                       "qcom,pm8916-regulators"
+                       "qcom,pm8941-regulators"
+
+- interrupts:
+       Usage: optional
+       Value type: <prop-encoded-array>
+       Definition: List of OCP interrupts.
+
+- interrupt-names:
+       Usage: required if 'interrupts' property present
+       Value type: <string-array>
+       Definition: List of strings defining the names of the
+                   interrupts in the 'interrupts' property 1-to-1.
+                   Supported values are "ocp-<regulator_name>", where
+                   <regulator_name> corresponds to a voltage switch
+                   type regulator.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_s5-supply:
+- vdd_s6-supply:
+- vdd_s7-supply:
+- vdd_s8-supply:
+       Usage: optional (pm8841 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_s4-supply:
+- vdd_l1_l3-supply:
+- vdd_l2-supply:
+- vdd_l4_l5_l6-supply:
+- vdd_l7-supply:
+- vdd_l8_l11_l14_l15_l16-supply:
+- vdd_l9_l10_l12_l13_l17_l18-supply:
+       Usage: optional (pm8916 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+- vdd_s1-supply:
+- vdd_s2-supply:
+- vdd_s3-supply:
+- vdd_l1_l3-supply:
+- vdd_l2_lvs_1_2_3-supply:
+- vdd_l4_l11-supply:
+- vdd_l5_l7-supply:
+- vdd_l6_l12_l14_l15-supply:
+- vdd_l8_l16_l18_19-supply:
+- vdd_l9_l10_l17_l22-supply:
+- vdd_l13_l20_l23_l24-supply:
+- vdd_l21-supply:
+- vin_5vs-supply:
+       Usage: optional (pm8941 only)
+       Value type: <phandle>
+       Definition: Reference to regulator supplying the input pin, as
+                   described in the data sheet.
+
+
+The regulator node houses sub-nodes for each regulator within the device. Each
+sub-node is identified using the node's name, with valid values listed for each
+of the PMICs below.
+
+pm8841:
+       s1, s2, s3, s4, s5, s6, s7, s8
+
+pm8916:
+       s1, s2, s3, s4, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13,
+       l14, l15, l16, l17, l18
+
+pm8941:
+       s1, s2, s3, l1, l2, l3, l4, l5, l6, l7, l8, l9, l10, l11, l12, l13, l14,
+       l15, l16, l17, l18, l19, l20, l21, l22, l23, l24, lvs1, lvs2, lvs3,
+       mvs1, mvs2
+
+The content of each sub-node is defined by the standard binding for regulators -
+see regulator.txt - with additional custom properties described below:
+
+- regulator-initial-mode:
+       Usage: optional
+       Value type: <u32>
+       Descrption: 1 = Set initial mode to high power mode (HPM), also referred
+                   to as NPM.  HPM consumes more ground current than LPM, but
+                   it can source significantly higher load current. HPM is not
+                   available on boost type regulators. For voltage switch type
+                   regulators, HPM implies that over current protection and
+                   soft start are active all the time. 0 = Set initial mode to
+                   low power mode (LPM).
+
+Example:
+
+       regulators {
+               compatible = "qcom,pm8941-regulators";
+               vdd_l1_l3-supply = <&s1>;
+
+               s1: s1 {
+                       regulator-min-microvolt = <1300000>;
+                       regulator-max-microvolt = <1400000>;
+               };
+
+               ...
+
+               l1: l1 {
+                       regulator-min-microvolt = <1225000>;
+                       regulator-max-microvolt = <1300000>;
+               };
+
+               ....
+       };
index abb26b58c83eea4eb60170f56700f9fa20aa8f88..db88feb28c0313bc9914eb2bcb4fc33dde264295 100644 (file)
@@ -7,18 +7,20 @@ Optional properties:
 - regulator-microvolt-offset: Offset applied to voltages to compensate for voltage drops
 - regulator-min-microamp: smallest current consumers may set
 - regulator-max-microamp: largest current consumers may set
+- regulator-input-current-limit-microamp: maximum input current regulator allows
 - regulator-always-on: boolean, regulator should never be disabled
 - regulator-boot-on: bootloader/firmware enabled regulator
 - regulator-allow-bypass: allow the regulator to go into bypass mode
 - <name>-supply: phandle to the parent supply/regulator node
 - regulator-ramp-delay: ramp delay for regulator(in uV/uS)
   For hardware which supports disabling ramp rate, it should be explicitly
-  intialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
+  initialised to zero (regulator-ramp-delay = <0>) for disabling ramp delay.
 - regulator-enable-ramp-delay: The time taken, in microseconds, for the supply
   rail to reach the target voltage, plus/minus whatever tolerance the board
   design requires. This property describes the total system ramp time
   required due to the combination of internal ramping of the regulator itself,
   and board design issues such as trace capacitance and load on the supply.
+- regulator-soft-start: Enable soft start so that voltage ramps slowly
 - 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.
@@ -37,6 +39,9 @@ Optional properties:
 - regulator-initial-mode: initial operating mode. The set of possible operating
   modes depends on the capabilities of every hardware so each device binding
   documentation explains which values the regulator supports.
+- regulator-system-load: Load in uA present on regulator that is not captured by
+  any consumer request.
+- regulator-pull-down: Enable pull down resistor when the regulator is disabled.
 
 Deprecated properties:
 - regulator-compatible: If a regulator chip contains multiple
index c3d15427adc7b2052191884aa58d3a069410016e..b100a63ff3b3f6398b649129559f8228f66265c2 100644 (file)
@@ -404,7 +404,7 @@ static int pm8607_regulator_probe(struct platform_device *pdev)
        return 0;
 }
 
-static struct platform_device_id pm8607_regulator_driver_ids[] = {
+static const struct platform_device_id pm8607_regulator_driver_ids[] = {
        {
                .name   = "88pm860x-regulator",
                .driver_data    = 0,
index a6f116aa523532b56194407438419c5bd5d238d9..bef3bde6971b0425d2632b54d2f1bb447c643ce4 100644 (file)
@@ -178,6 +178,16 @@ config REGULATOR_DA9055
          This driver can also be built as a module. If so, the module
          will be called da9055-regulator.
 
+config REGULATOR_DA9062
+       tristate "Dialog Semiconductor DA9062 regulators"
+       depends on MFD_DA9062
+       help
+         Say y here to support the BUCKs and LDOs regulators found on
+         DA9062 PMICs.
+
+         This driver can also be built as a module. If so, the module
+         will be called da9062-regulator.
+
 config REGULATOR_DA9063
        tristate "Dialog Semiconductor DA9063 regulators"
        depends on MFD_DA9063
@@ -233,7 +243,7 @@ config REGULATOR_FAN53555
 
 config REGULATOR_GPIO
        tristate "GPIO regulator support"
-       depends on GPIOLIB
+       depends on GPIOLIB || COMPILE_TEST
        help
          This driver provides support for regulators that can be
          controlled via gpios.
@@ -512,6 +522,17 @@ config REGULATOR_QCOM_RPM
          Qualcomm RPM as a module. The module will be named
          "qcom_rpm-regulator".
 
+config REGULATOR_QCOM_SPMI
+       tristate "Qualcomm SPMI regulator driver"
+       depends on SPMI || COMPILE_TEST
+       help
+         If you say yes to this option, support will be included for the
+         regulators found in Qualcomm SPMI PMICs.
+
+         Say M here if you want to include support for the regulators on the
+         Qualcomm SPMI PMICs as a module. The module will be named
+         "qcom_spmi-regulator".
+
 config REGULATOR_RC5T583
        tristate "RICOH RC5T583 Power regulators"
        depends on MFD_RC5T583
index 2c4da15e1545a71076616fbd94f3648ad453473a..91bf76267404c9b8a9260dba5e0a0c8a793247e6 100644 (file)
@@ -25,6 +25,7 @@ obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o
 obj-$(CONFIG_REGULATOR_DA903X) += da903x.o
 obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o
 obj-$(CONFIG_REGULATOR_DA9055) += da9055-regulator.o
+obj-$(CONFIG_REGULATOR_DA9062) += da9062-regulator.o
 obj-$(CONFIG_REGULATOR_DA9063) += da9063-regulator.o
 obj-$(CONFIG_REGULATOR_DA9210) += da9210-regulator.o
 obj-$(CONFIG_REGULATOR_DA9211) += da9211-regulator.o
@@ -61,6 +62,7 @@ obj-$(CONFIG_REGULATOR_MC13892) += mc13892-regulator.o
 obj-$(CONFIG_REGULATOR_MC13XXX_CORE) +=  mc13xxx-regulator-core.o
 obj-$(CONFIG_REGULATOR_MT6397) += mt6397-regulator.o
 obj-$(CONFIG_REGULATOR_QCOM_RPM) += qcom_rpm-regulator.o
+obj-$(CONFIG_REGULATOR_QCOM_SPMI) += qcom_spmi-regulator.o
 obj-$(CONFIG_REGULATOR_PALMAS) += palmas-regulator.o
 obj-$(CONFIG_REGULATOR_PFUZE100) += pfuze100-regulator.o
 obj-$(CONFIG_REGULATOR_PWM) += pwm-regulator.o
index a1d07d347c20e30af065d204d94a314d18c408e3..90941632efa9b1868033cb256820137f8c406542 100644 (file)
@@ -178,6 +178,16 @@ static const struct regulator_init_data arizona_ldo1_default = {
        .num_consumer_supplies = 1,
 };
 
+static const struct regulator_init_data arizona_ldo1_wm5110 = {
+       .constraints = {
+               .min_uV = 1175000,
+               .max_uV = 1200000,
+               .valid_ops_mask = REGULATOR_CHANGE_STATUS |
+                                 REGULATOR_CHANGE_VOLTAGE,
+       },
+       .num_consumer_supplies = 1,
+};
+
 static int arizona_ldo1_of_get_pdata(struct arizona *arizona,
                                     struct regulator_config *config,
                                     const struct regulator_desc *desc)
@@ -243,6 +253,11 @@ static int arizona_ldo1_probe(struct platform_device *pdev)
                desc = &arizona_ldo1_hc;
                ldo1->init_data = arizona_ldo1_dvfs;
                break;
+       case WM5110:
+       case WM8280:
+               desc = &arizona_ldo1;
+               ldo1->init_data = arizona_ldo1_wm5110;
+               break;
        default:
                desc = &arizona_ldo1;
                ldo1->init_data = arizona_ldo1_default;
index 443eaab933fcfe680d5e402f72bbe7c55c3ae391..c9f72019bd689afbb4e51528932689dc097b191b 100644 (file)
@@ -678,6 +678,8 @@ static int drms_uA_update(struct regulator_dev *rdev)
        list_for_each_entry(sibling, &rdev->consumer_list, list)
                current_uA += sibling->uA_load;
 
+       current_uA += rdev->constraints->system_load;
+
        if (rdev->desc->ops->set_load) {
                /* set the optimum mode for our new total regulator load */
                err = rdev->desc->ops->set_load(rdev, current_uA);
@@ -779,59 +781,64 @@ static int suspend_prepare(struct regulator_dev *rdev, suspend_state_t state)
 static void print_constraints(struct regulator_dev *rdev)
 {
        struct regulation_constraints *constraints = rdev->constraints;
-       char buf[80] = "";
+       char buf[160] = "";
+       size_t len = sizeof(buf) - 1;
        int count = 0;
        int ret;
 
        if (constraints->min_uV && constraints->max_uV) {
                if (constraints->min_uV == constraints->max_uV)
-                       count += sprintf(buf + count, "%d mV ",
-                                        constraints->min_uV / 1000);
+                       count += scnprintf(buf + count, len - count, "%d mV ",
+                                          constraints->min_uV / 1000);
                else
-                       count += sprintf(buf + count, "%d <--> %d mV ",
-                                        constraints->min_uV / 1000,
-                                        constraints->max_uV / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "%d <--> %d mV ",
+                                          constraints->min_uV / 1000,
+                                          constraints->max_uV / 1000);
        }
 
        if (!constraints->min_uV ||
            constraints->min_uV != constraints->max_uV) {
                ret = _regulator_get_voltage(rdev);
                if (ret > 0)
-                       count += sprintf(buf + count, "at %d mV ", ret / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "at %d mV ", ret / 1000);
        }
 
        if (constraints->uV_offset)
-               count += sprintf(buf, "%dmV offset ",
-                                constraints->uV_offset / 1000);
+               count += scnprintf(buf + count, len - count, "%dmV offset ",
+                                  constraints->uV_offset / 1000);
 
        if (constraints->min_uA && constraints->max_uA) {
                if (constraints->min_uA == constraints->max_uA)
-                       count += sprintf(buf + count, "%d mA ",
-                                        constraints->min_uA / 1000);
+                       count += scnprintf(buf + count, len - count, "%d mA ",
+                                          constraints->min_uA / 1000);
                else
-                       count += sprintf(buf + count, "%d <--> %d mA ",
-                                        constraints->min_uA / 1000,
-                                        constraints->max_uA / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "%d <--> %d mA ",
+                                          constraints->min_uA / 1000,
+                                          constraints->max_uA / 1000);
        }
 
        if (!constraints->min_uA ||
            constraints->min_uA != constraints->max_uA) {
                ret = _regulator_get_current_limit(rdev);
                if (ret > 0)
-                       count += sprintf(buf + count, "at %d mA ", ret / 1000);
+                       count += scnprintf(buf + count, len - count,
+                                          "at %d mA ", ret / 1000);
        }
 
        if (constraints->valid_modes_mask & REGULATOR_MODE_FAST)
-               count += sprintf(buf + count, "fast ");
+               count += scnprintf(buf + count, len - count, "fast ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_NORMAL)
-               count += sprintf(buf + count, "normal ");
+               count += scnprintf(buf + count, len - count, "normal ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_IDLE)
-               count += sprintf(buf + count, "idle ");
+               count += scnprintf(buf + count, len - count, "idle ");
        if (constraints->valid_modes_mask & REGULATOR_MODE_STANDBY)
-               count += sprintf(buf + count, "standby");
+               count += scnprintf(buf + count, len - count, "standby");
 
        if (!count)
-               sprintf(buf, "no parameters");
+               scnprintf(buf, len, "no parameters");
 
        rdev_dbg(rdev, "%s\n", buf);
 
@@ -1006,6 +1013,15 @@ static int set_machine_constraints(struct regulator_dev *rdev,
        if (ret != 0)
                goto out;
 
+       if (rdev->constraints->ilim_uA && ops->set_input_current_limit) {
+               ret = ops->set_input_current_limit(rdev,
+                                                  rdev->constraints->ilim_uA);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set input limit\n");
+                       goto out;
+               }
+       }
+
        /* do we need to setup our suspend state */
        if (rdev->constraints->initial_state) {
                ret = suspend_prepare(rdev, rdev->constraints->initial_state);
@@ -1049,6 +1065,22 @@ static int set_machine_constraints(struct regulator_dev *rdev,
                }
        }
 
+       if (rdev->constraints->pull_down && ops->set_pull_down) {
+               ret = ops->set_pull_down(rdev);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set pull down\n");
+                       goto out;
+               }
+       }
+
+       if (rdev->constraints->soft_start && ops->set_soft_start) {
+               ret = ops->set_soft_start(rdev);
+               if (ret < 0) {
+                       rdev_err(rdev, "failed to set soft start\n");
+                       goto out;
+               }
+       }
+
        print_constraints(rdev);
        return 0;
 out:
@@ -1192,10 +1224,10 @@ static struct regulator *create_regulator(struct regulator_dev *rdev,
                if (regulator->supply_name == NULL)
                        goto overflow_err;
 
-               err = sysfs_create_link(&rdev->dev.kobj, &dev->kobj,
+               err = sysfs_create_link_nowarn(&rdev->dev.kobj, &dev->kobj,
                                        buf);
                if (err) {
-                       rdev_warn(rdev, "could not add device link %s err %d\n",
+                       rdev_dbg(rdev, "could not add device link %s err %d\n",
                                  dev->kobj.name, err);
                        /* non-fatal */
                }
diff --git a/drivers/regulator/da9062-regulator.c b/drivers/regulator/da9062-regulator.c
new file mode 100644 (file)
index 0000000..dd76da0
--- /dev/null
@@ -0,0 +1,842 @@
+/*
+ * da9062-regulator.c - REGULATOR device driver for DA9062
+ * Copyright (C) 2015  Dialog Semiconductor Ltd.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/err.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/da9062/core.h>
+#include <linux/mfd/da9062/registers.h>
+
+/* Regulator IDs */
+enum {
+       DA9062_ID_BUCK1,
+       DA9062_ID_BUCK2,
+       DA9062_ID_BUCK3,
+       DA9062_ID_BUCK4,
+       DA9062_ID_LDO1,
+       DA9062_ID_LDO2,
+       DA9062_ID_LDO3,
+       DA9062_ID_LDO4,
+       DA9062_MAX_REGULATORS,
+};
+
+/* Regulator capabilities and registers description */
+struct da9062_regulator_info {
+       struct regulator_desc desc;
+       /* Current limiting */
+       unsigned int n_current_limits;
+       const int *current_limits;
+       /* Main register fields */
+       struct reg_field mode;
+       struct reg_field suspend;
+       struct reg_field sleep;
+       struct reg_field suspend_sleep;
+       unsigned int suspend_vsel_reg;
+       struct reg_field ilimit;
+       /* Event detection bit */
+       struct reg_field oc_event;
+};
+
+/* Single regulator settings */
+struct da9062_regulator {
+       struct regulator_desc                   desc;
+       struct regulator_dev                    *rdev;
+       struct da9062                           *hw;
+       const struct da9062_regulator_info      *info;
+
+       struct regmap_field                     *mode;
+       struct regmap_field                     *suspend;
+       struct regmap_field                     *sleep;
+       struct regmap_field                     *suspend_sleep;
+       struct regmap_field                     *ilimit;
+};
+
+/* Encapsulates all information for the regulators driver */
+struct da9062_regulators {
+       int                                     irq_ldo_lim;
+       unsigned                                n_regulators;
+       /* Array size to be defined during init. Keep at end. */
+       struct da9062_regulator                 regulator[0];
+};
+
+/* BUCK modes */
+enum {
+       BUCK_MODE_MANUAL,       /* 0 */
+       BUCK_MODE_SLEEP,        /* 1 */
+       BUCK_MODE_SYNC,         /* 2 */
+       BUCK_MODE_AUTO          /* 3 */
+};
+
+/* Regulator operations */
+
+/* Current limits array (in uA) BUCK1 and BUCK3.
+   Entry indexes corresponds to register values. */
+static const int da9062_buck_a_limits[] = {
+        500000,  600000,  700000,  800000,  900000, 1000000, 1100000, 1200000,
+       1300000, 1400000, 1500000, 1600000, 1700000, 1800000, 1900000, 2000000
+};
+
+/* Current limits array (in uA) for BUCK2.
+   Entry indexes corresponds to register values. */
+static const int da9062_buck_b_limits[] = {
+       1500000, 1600000, 1700000, 1800000, 1900000, 2000000, 2100000, 2200000,
+       2300000, 2400000, 2500000, 2600000, 2700000, 2800000, 2900000, 3000000
+};
+
+static int da9062_set_current_limit(struct regulator_dev *rdev,
+                                   int min_ua, int max_ua)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       int n, tval;
+
+       for (n = 0; n < rinfo->n_current_limits; n++) {
+               tval = rinfo->current_limits[n];
+               if (tval >= min_ua && tval <= max_ua)
+                       return regmap_field_write(regl->ilimit, n);
+       }
+
+       return -EINVAL;
+}
+
+static int da9062_get_current_limit(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       unsigned int sel;
+       int ret;
+
+       ret = regmap_field_read(regl->ilimit, &sel);
+       if (ret < 0)
+               return ret;
+
+       if (sel >= rinfo->n_current_limits)
+               sel = rinfo->n_current_limits - 1;
+
+       return rinfo->current_limits[sel];
+}
+
+static int da9062_buck_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               val = BUCK_MODE_SYNC;
+               break;
+       case REGULATOR_MODE_NORMAL:
+               val = BUCK_MODE_AUTO;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = BUCK_MODE_SLEEP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->mode, val);
+}
+
+/*
+ * Bucks use single mode register field for normal operation
+ * and suspend state.
+ * There are 3 modes to map to: FAST, NORMAL, and STANDBY.
+ */
+
+static unsigned da9062_buck_get_mode(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       struct regmap_field *field;
+       unsigned int val, mode = 0;
+       int ret;
+
+       ret = regmap_field_read(regl->mode, &val);
+       if (ret < 0)
+               return ret;
+
+       switch (val) {
+       default:
+       case BUCK_MODE_MANUAL:
+               mode = REGULATOR_MODE_FAST | REGULATOR_MODE_STANDBY;
+               /* Sleep flag bit decides the mode */
+               break;
+       case BUCK_MODE_SLEEP:
+               return REGULATOR_MODE_STANDBY;
+       case BUCK_MODE_SYNC:
+               return REGULATOR_MODE_FAST;
+       case BUCK_MODE_AUTO:
+               return REGULATOR_MODE_NORMAL;
+       }
+
+       /* Detect current regulator state */
+       ret = regmap_field_read(regl->suspend, &val);
+       if (ret < 0)
+               return 0;
+
+       /* Read regulator mode from proper register, depending on state */
+       if (val)
+               field = regl->suspend_sleep;
+       else
+               field = regl->sleep;
+
+       ret = regmap_field_read(field, &val);
+       if (ret < 0)
+               return 0;
+
+       if (val)
+               mode &= REGULATOR_MODE_STANDBY;
+       else
+               mode &= REGULATOR_MODE_NORMAL | REGULATOR_MODE_FAST;
+
+       return mode;
+}
+
+/*
+ * LDOs use sleep flags - one for normal and one for suspend state.
+ * There are 2 modes to map to: NORMAL and STANDBY (sleep) for each state.
+ */
+
+static int da9062_ldo_set_mode(struct regulator_dev *rdev, unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               val = 0;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->sleep, val);
+}
+
+static unsigned da9062_ldo_get_mode(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       struct regmap_field *field;
+       int ret, val;
+
+       /* Detect current regulator state */
+       ret = regmap_field_read(regl->suspend, &val);
+       if (ret < 0)
+               return 0;
+
+       /* Read regulator mode from proper register, depending on state */
+       if (val)
+               field = regl->suspend_sleep;
+       else
+               field = regl->sleep;
+
+       ret = regmap_field_read(field, &val);
+       if (ret < 0)
+               return 0;
+
+       if (val)
+               return REGULATOR_MODE_STANDBY;
+       else
+               return REGULATOR_MODE_NORMAL;
+}
+
+static int da9062_buck_get_status(struct regulator_dev *rdev)
+{
+       int ret = regulator_is_enabled_regmap(rdev);
+
+       if (ret == 0) {
+               ret = REGULATOR_STATUS_OFF;
+       } else if (ret > 0) {
+               ret = da9062_buck_get_mode(rdev);
+               if (ret > 0)
+                       ret = regulator_mode_to_status(ret);
+               else if (ret == 0)
+                       ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int da9062_ldo_get_status(struct regulator_dev *rdev)
+{
+       int ret = regulator_is_enabled_regmap(rdev);
+
+       if (ret == 0) {
+               ret = REGULATOR_STATUS_OFF;
+       } else if (ret > 0) {
+               ret = da9062_ldo_get_mode(rdev);
+               if (ret > 0)
+                       ret = regulator_mode_to_status(ret);
+               else if (ret == 0)
+                       ret = -EIO;
+       }
+
+       return ret;
+}
+
+static int da9062_set_suspend_voltage(struct regulator_dev *rdev, int uv)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       const struct da9062_regulator_info *rinfo = regl->info;
+       int ret, sel;
+
+       sel = regulator_map_voltage_linear(rdev, uv, uv);
+       if (sel < 0)
+               return sel;
+
+       sel <<= ffs(rdev->desc->vsel_mask) - 1;
+
+       ret = regmap_update_bits(regl->hw->regmap, rinfo->suspend_vsel_reg,
+                                rdev->desc->vsel_mask, sel);
+
+       return ret;
+}
+
+static int da9062_suspend_enable(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+       return regmap_field_write(regl->suspend, 1);
+}
+
+static int da9062_suspend_disable(struct regulator_dev *rdev)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+
+       return regmap_field_write(regl->suspend, 0);
+}
+
+static int da9062_buck_set_suspend_mode(struct regulator_dev *rdev,
+                                       unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       int val;
+
+       switch (mode) {
+       case REGULATOR_MODE_FAST:
+               val = BUCK_MODE_SYNC;
+               break;
+       case REGULATOR_MODE_NORMAL:
+               val = BUCK_MODE_AUTO;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = BUCK_MODE_SLEEP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->mode, val);
+}
+
+static int da9062_ldo_set_suspend_mode(struct regulator_dev *rdev,
+                                               unsigned mode)
+{
+       struct da9062_regulator *regl = rdev_get_drvdata(rdev);
+       unsigned val;
+
+       switch (mode) {
+       case REGULATOR_MODE_NORMAL:
+               val = 0;
+               break;
+       case REGULATOR_MODE_STANDBY:
+               val = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_field_write(regl->suspend_sleep, val);
+}
+
+static struct regulator_ops da9062_buck_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .set_current_limit      = da9062_set_current_limit,
+       .get_current_limit      = da9062_get_current_limit,
+       .set_mode               = da9062_buck_set_mode,
+       .get_mode               = da9062_buck_get_mode,
+       .get_status             = da9062_buck_get_status,
+       .set_suspend_voltage    = da9062_set_suspend_voltage,
+       .set_suspend_enable     = da9062_suspend_enable,
+       .set_suspend_disable    = da9062_suspend_disable,
+       .set_suspend_mode       = da9062_buck_set_suspend_mode,
+};
+
+static struct regulator_ops da9062_ldo_ops = {
+       .enable                 = regulator_enable_regmap,
+       .disable                = regulator_disable_regmap,
+       .is_enabled             = regulator_is_enabled_regmap,
+       .get_voltage_sel        = regulator_get_voltage_sel_regmap,
+       .set_voltage_sel        = regulator_set_voltage_sel_regmap,
+       .list_voltage           = regulator_list_voltage_linear,
+       .set_mode               = da9062_ldo_set_mode,
+       .get_mode               = da9062_ldo_get_mode,
+       .get_status             = da9062_ldo_get_status,
+       .set_suspend_voltage    = da9062_set_suspend_voltage,
+       .set_suspend_enable     = da9062_suspend_enable,
+       .set_suspend_disable    = da9062_suspend_disable,
+       .set_suspend_mode       = da9062_ldo_set_suspend_mode,
+};
+
+/* Regulator information */
+static const struct da9062_regulator_info local_regulator_info[] = {
+       {
+               .desc.id = DA9062_ID_BUCK1,
+               .desc.name = "DA9062 BUCK1",
+               .desc.of_match = of_match_ptr("buck1"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (300) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1570) - (300))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK1_CONT,
+               .desc.enable_mask = DA9062AA_BUCK1_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK1_A,
+               .desc.vsel_mask = DA9062AA_VBUCK1_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK1_A,
+                       __builtin_ffs((int)DA9062AA_BUCK1_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK1_B,
+                       __builtin_ffs((int)DA9062AA_BUCK1_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK1_B,
+               .mode = REG_FIELD(DA9062AA_BUCK1_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK1_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK1_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK1_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+                       __builtin_ffs((int)DA9062AA_BUCK1_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK1_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK2,
+               .desc.name = "DA9062 BUCK2",
+               .desc.of_match = of_match_ptr("buck2"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (300) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1570) - (300))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK2_CONT,
+               .desc.enable_mask = DA9062AA_BUCK2_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK2_A,
+               .desc.vsel_mask = DA9062AA_VBUCK2_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK2_A,
+                       __builtin_ffs((int)DA9062AA_BUCK2_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK2_B,
+                       __builtin_ffs((int)DA9062AA_BUCK2_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK2_B,
+               .mode = REG_FIELD(DA9062AA_BUCK2_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK2_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK2_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK2_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_C,
+                       __builtin_ffs((int)DA9062AA_BUCK2_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK2_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK3,
+               .desc.name = "DA9062 BUCK3",
+               .desc.of_match = of_match_ptr("buck3"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (800) * 1000,
+               .desc.uV_step = (20) * 1000,
+               .desc.n_voltages = ((3340) - (800))/(20) + 1,
+               .current_limits = da9062_buck_b_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_b_limits),
+               .desc.enable_reg = DA9062AA_BUCK3_CONT,
+               .desc.enable_mask = DA9062AA_BUCK3_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK3_A,
+               .desc.vsel_mask = DA9062AA_VBUCK3_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK3_A,
+                       __builtin_ffs((int)DA9062AA_BUCK3_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK3_B,
+                       __builtin_ffs((int)DA9062AA_BUCK3_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK3_B,
+               .mode = REG_FIELD(DA9062AA_BUCK3_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK3_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK3_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK3_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_A,
+                       __builtin_ffs((int)DA9062AA_BUCK3_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK3_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_BUCK4,
+               .desc.name = "DA9062 BUCK4",
+               .desc.of_match = of_match_ptr("buck4"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_buck_ops,
+               .desc.min_uV = (530) * 1000,
+               .desc.uV_step = (10) * 1000,
+               .desc.n_voltages = ((1800) - (530))/(10) + 1,
+               .current_limits = da9062_buck_a_limits,
+               .n_current_limits = ARRAY_SIZE(da9062_buck_a_limits),
+               .desc.enable_reg = DA9062AA_BUCK4_CONT,
+               .desc.enable_mask = DA9062AA_BUCK4_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VBUCK4_A,
+               .desc.vsel_mask = DA9062AA_VBUCK4_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VBUCK4_A,
+                       __builtin_ffs((int)DA9062AA_BUCK4_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VBUCK4_B,
+                       __builtin_ffs((int)DA9062AA_BUCK4_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VBUCK4_B,
+               .mode = REG_FIELD(DA9062AA_BUCK4_CFG,
+                       __builtin_ffs((int)DA9062AA_BUCK4_MODE_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_MODE_MASK)) - 1),
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VBUCK4_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VBUCK4_SEL_MASK)) - 1),
+               .ilimit = REG_FIELD(DA9062AA_BUCK_ILIM_B,
+                       __builtin_ffs((int)DA9062AA_BUCK4_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_BUCK4_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO1,
+               .desc.name = "DA9062 LDO1",
+               .desc.of_match = of_match_ptr("ldo1"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO1_CONT,
+               .desc.enable_mask = DA9062AA_LDO1_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO1_A,
+               .desc.vsel_mask = DA9062AA_VLDO1_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO1_A,
+                       __builtin_ffs((int)DA9062AA_LDO1_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO1_B,
+                       __builtin_ffs((int)DA9062AA_LDO1_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO1_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO1_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO1_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO1_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO1_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO2,
+               .desc.name = "DA9062 LDO2",
+               .desc.of_match = of_match_ptr("ldo2"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (600))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO2_CONT,
+               .desc.enable_mask = DA9062AA_LDO2_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO2_A,
+               .desc.vsel_mask = DA9062AA_VLDO2_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO2_A,
+                       __builtin_ffs((int)DA9062AA_LDO2_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO2_B,
+                       __builtin_ffs((int)DA9062AA_LDO2_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO2_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO2_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO2_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO2_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO2_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO3,
+               .desc.name = "DA9062 LDO3",
+               .desc.of_match = of_match_ptr("ldo3"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO3_CONT,
+               .desc.enable_mask = DA9062AA_LDO3_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO3_A,
+               .desc.vsel_mask = DA9062AA_VLDO3_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO3_A,
+                       __builtin_ffs((int)DA9062AA_LDO3_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO3_B,
+                       __builtin_ffs((int)DA9062AA_LDO3_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO3_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO3_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO3_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO3_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO3_ILIM_MASK)) - 1),
+       },
+       {
+               .desc.id = DA9062_ID_LDO4,
+               .desc.name = "DA9062 LDO4",
+               .desc.of_match = of_match_ptr("ldo4"),
+               .desc.regulators_node = of_match_ptr("regulators"),
+               .desc.ops = &da9062_ldo_ops,
+               .desc.min_uV = (900) * 1000,
+               .desc.uV_step = (50) * 1000,
+               .desc.n_voltages = ((3600) - (900))/(50) + 1,
+               .desc.enable_reg = DA9062AA_LDO4_CONT,
+               .desc.enable_mask = DA9062AA_LDO4_EN_MASK,
+               .desc.vsel_reg = DA9062AA_VLDO4_A,
+               .desc.vsel_mask = DA9062AA_VLDO4_A_MASK,
+               .desc.linear_min_sel = 0,
+               .sleep = REG_FIELD(DA9062AA_VLDO4_A,
+                       __builtin_ffs((int)DA9062AA_LDO4_SL_A_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_SL_A_MASK)) - 1),
+               .suspend_sleep = REG_FIELD(DA9062AA_VLDO4_B,
+                       __builtin_ffs((int)DA9062AA_LDO4_SL_B_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_SL_B_MASK)) - 1),
+               .suspend_vsel_reg = DA9062AA_VLDO4_B,
+               .suspend = REG_FIELD(DA9062AA_DVC_1,
+                       __builtin_ffs((int)DA9062AA_VLDO4_SEL_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_VLDO4_SEL_MASK)) - 1),
+               .oc_event = REG_FIELD(DA9062AA_STATUS_D,
+                       __builtin_ffs((int)DA9062AA_LDO4_ILIM_MASK) - 1,
+                       sizeof(unsigned int) * 8 -
+                       __builtin_clz((DA9062AA_LDO4_ILIM_MASK)) - 1),
+       },
+};
+
+/* Regulator interrupt handlers */
+static irqreturn_t da9062_ldo_lim_event(int irq, void *data)
+{
+       struct da9062_regulators *regulators = data;
+       struct da9062 *hw = regulators->regulator[0].hw;
+       struct da9062_regulator *regl;
+       int handled = IRQ_NONE;
+       int bits, i, ret;
+
+       ret = regmap_read(hw->regmap, DA9062AA_STATUS_D, &bits);
+       if (ret < 0) {
+               dev_err(hw->dev,
+                       "Failed to read LDO overcurrent indicator\n");
+               goto ldo_lim_error;
+       }
+
+       for (i = regulators->n_regulators - 1; i >= 0; i--) {
+               regl = &regulators->regulator[i];
+               if (regl->info->oc_event.reg != DA9062AA_STATUS_D)
+                       continue;
+
+               if (BIT(regl->info->oc_event.lsb) & bits) {
+                       regulator_notifier_call_chain(regl->rdev,
+                                       REGULATOR_EVENT_OVER_CURRENT, NULL);
+                       handled = IRQ_HANDLED;
+               }
+       }
+
+ldo_lim_error:
+       return handled;
+}
+
+static int da9062_regulator_probe(struct platform_device *pdev)
+{
+       struct da9062 *chip = dev_get_drvdata(pdev->dev.parent);
+       struct da9062_regulators *regulators;
+       struct da9062_regulator *regl;
+       struct regulator_config config = { };
+       int irq, n, ret;
+       size_t size;
+
+       /* Allocate memory required by usable regulators */
+       size = sizeof(struct da9062_regulators) +
+               DA9062_MAX_REGULATORS * sizeof(struct da9062_regulator);
+       regulators = devm_kzalloc(&pdev->dev, size, GFP_KERNEL);
+       if (!regulators)
+               return -ENOMEM;
+
+       regulators->n_regulators = DA9062_MAX_REGULATORS;
+       platform_set_drvdata(pdev, regulators);
+
+       n = 0;
+       while (n < regulators->n_regulators) {
+               /* Initialise regulator structure */
+               regl = &regulators->regulator[n];
+               regl->hw = chip;
+               regl->info = &local_regulator_info[n];
+               regl->desc = regl->info->desc;
+               regl->desc.type = REGULATOR_VOLTAGE;
+               regl->desc.owner = THIS_MODULE;
+
+               if (regl->info->mode.reg)
+                       regl->mode = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->mode);
+               if (regl->info->suspend.reg)
+                       regl->suspend = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->suspend);
+               if (regl->info->sleep.reg)
+                       regl->sleep = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->sleep);
+               if (regl->info->suspend_sleep.reg)
+                       regl->suspend_sleep = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->suspend_sleep);
+               if (regl->info->ilimit.reg)
+                       regl->ilimit = devm_regmap_field_alloc(
+                                       &pdev->dev,
+                                       chip->regmap,
+                                       regl->info->ilimit);
+
+               /* Register regulator */
+               memset(&config, 0, sizeof(config));
+               config.dev = chip->dev;
+               config.driver_data = regl;
+               config.regmap = chip->regmap;
+
+               regl->rdev = devm_regulator_register(&pdev->dev, &regl->desc,
+                                                    &config);
+               if (IS_ERR(regl->rdev)) {
+                       dev_err(&pdev->dev,
+                               "Failed to register %s regulator\n",
+                               regl->desc.name);
+                       return PTR_ERR(regl->rdev);
+               }
+
+               n++;
+       }
+
+       /* LDOs overcurrent event support */
+       irq = platform_get_irq_byname(pdev, "LDO_LIM");
+       if (irq < 0) {
+               dev_err(&pdev->dev, "Failed to get IRQ.\n");
+               return irq;
+       }
+       regulators->irq_ldo_lim = irq;
+
+       ret = devm_request_threaded_irq(&pdev->dev, irq,
+                                       NULL, da9062_ldo_lim_event,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       "LDO_LIM", regulators);
+       if (ret) {
+               dev_warn(&pdev->dev,
+                        "Failed to request LDO_LIM IRQ.\n");
+               regulators->irq_ldo_lim = -ENXIO;
+       }
+
+       return 0;
+}
+
+static struct platform_driver da9062_regulator_driver = {
+       .driver = {
+               .name = "da9062-regulators",
+               .owner = THIS_MODULE,
+       },
+       .probe = da9062_regulator_probe,
+};
+
+static int __init da9062_regulator_init(void)
+{
+       return platform_driver_register(&da9062_regulator_driver);
+}
+subsys_initcall(da9062_regulator_init);
+
+static void __exit da9062_regulator_cleanup(void)
+{
+       platform_driver_unregister(&da9062_regulator_driver);
+}
+module_exit(da9062_regulator_cleanup);
+
+/* Module information */
+MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
+MODULE_DESCRIPTION("REGULATOR device driver for Dialog DA9062");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:da9062-regulators");
index 31c2c593ae0b76886dadc0df2526c5c8341c34ad..aed1ad3dc964e231c76dc4131a42316f2b13a5fe 100644 (file)
@@ -117,9 +117,6 @@ struct da9063_regulator {
 
 /* Encapsulates all information for the regulators driver */
 struct da9063_regulators {
-       int                                     irq_ldo_lim;
-       int                                     irq_uvov;
-
        unsigned                                n_regulators;
        /* Array size to be defined during init. Keep at end. */
        struct da9063_regulator                 regulator[0];
@@ -867,35 +864,23 @@ static int da9063_regulator_probe(struct platform_device *pdev)
                return irq;
        }
 
-       ret = request_threaded_irq(irq,
+       ret = devm_request_threaded_irq(&pdev->dev, irq,
                                NULL, da9063_ldo_lim_event,
                                IRQF_TRIGGER_LOW | IRQF_ONESHOT,
                                "LDO_LIM", regulators);
        if (ret) {
-               dev_err(&pdev->dev,
-                               "Failed to request LDO_LIM IRQ.\n");
-               regulators->irq_ldo_lim = -ENXIO;
+               dev_err(&pdev->dev, "Failed to request LDO_LIM IRQ.\n");
+               return ret;
        }
 
        return 0;
 }
 
-static int da9063_regulator_remove(struct platform_device *pdev)
-{
-       struct da9063_regulators *regulators = platform_get_drvdata(pdev);
-
-       free_irq(regulators->irq_ldo_lim, regulators);
-       free_irq(regulators->irq_uvov, regulators);
-
-       return 0;
-}
-
 static struct platform_driver da9063_regulator_driver = {
        .driver = {
                .name = DA9063_DRVNAME_REGULATORS,
        },
        .probe = da9063_regulator_probe,
-       .remove = da9063_regulator_remove,
 };
 
 static int __init da9063_regulator_init(void)
index 3c25db89a021af927f183b9f363b270b76e691c4..42865681c00bfb18df9bc5085a0b84f6fd8a0851 100644 (file)
@@ -182,6 +182,7 @@ static int fan53555_set_ramp(struct regulator_dev *rdev, int ramp)
 static struct regulator_ops fan53555_regulator_ops = {
        .set_voltage_sel = regulator_set_voltage_sel_regmap,
        .get_voltage_sel = regulator_get_voltage_sel_regmap,
+       .set_voltage_time_sel = regulator_set_voltage_time_sel,
        .map_voltage = regulator_map_voltage_linear,
        .list_voltage = regulator_list_voltage_linear,
        .set_suspend_voltage = fan53555_set_suspend_voltage,
index cbc39096c78d436f8206aa7107811bc43da6fd20..3bbb32680a94265069eac502d1d2b679e22fa673 100644 (file)
@@ -275,7 +275,7 @@ int regulator_map_voltage_linear(struct regulator_dev *rdev,
 EXPORT_SYMBOL_GPL(regulator_map_voltage_linear);
 
 /**
- * regulator_map_voltage_linear - map_voltage() for multiple linear ranges
+ * regulator_map_voltage_linear_range - map_voltage() for multiple linear ranges
  *
  * @rdev: Regulator to operate on
  * @min_uV: Lower bound for voltage
index 4a415d4ee4635a45074205c5a5cdda998c09a720..d6773da925bad00f3bc227294255f8786c2eaf9b 100644 (file)
@@ -419,20 +419,16 @@ static int lp8755_int_config(struct lp8755_chip *pchip)
        }
 
        ret = lp8755_read(pchip, 0x0F, &regval);
-       if (ret < 0)
-               goto err_i2c;
-       pchip->irqmask = regval;
-       ret = request_threaded_irq(pchip->irq, NULL, lp8755_irq_handler,
-                                  IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
-                                  "lp8755-irq", pchip);
-       if (ret)
+       if (ret < 0) {
+               dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
                return ret;
+       }
 
-       return ret;
-
-err_i2c:
-       dev_err(pchip->dev, "i2c acceess error %s\n", __func__);
-       return ret;
+       pchip->irqmask = regval;
+       return devm_request_threaded_irq(pchip->dev, pchip->irq, NULL,
+                                        lp8755_irq_handler,
+                                        IRQF_TRIGGER_FALLING | IRQF_ONESHOT,
+                                        "lp8755-irq", pchip);
 }
 
 static const struct regmap_config lp8755_regmap = {
@@ -514,9 +510,6 @@ static int lp8755_remove(struct i2c_client *client)
        for (icnt = 0; icnt < LP8755_BUCK_MAX; icnt++)
                lp8755_write(pchip, icnt, 0x00);
 
-       if (pchip->irq != 0)
-               free_irq(pchip->irq, pchip);
-
        return 0;
 }
 
index b3678d289619330ffca48f338b9ddf362cd76986..b2daa6641417caecedbd43fde480ef8159cd3652 100644 (file)
@@ -100,31 +100,34 @@ static struct regulator_ops max14577_charger_ops = {
        .set_current_limit      = max14577_reg_set_current_limit,
 };
 
+#define MAX14577_SAFEOUT_REG   { \
+       .name           = "SAFEOUT", \
+       .of_match       = of_match_ptr("SAFEOUT"), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX14577_SAFEOUT, \
+       .ops            = &max14577_safeout_ops, \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = 1, \
+       .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE, \
+       .enable_reg     = MAX14577_REG_CONTROL2, \
+       .enable_mask    = CTRL2_SFOUTORD_MASK, \
+}
+#define MAX14577_CHARGER_REG   { \
+       .name           = "CHARGER", \
+       .of_match       = of_match_ptr("CHARGER"), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX14577_CHARGER, \
+       .ops            = &max14577_charger_ops, \
+       .type           = REGULATOR_CURRENT, \
+       .owner          = THIS_MODULE, \
+       .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2, \
+       .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK, \
+}
+
 static const struct regulator_desc max14577_supported_regulators[] = {
-       [MAX14577_SAFEOUT] = {
-               .name           = "SAFEOUT",
-               .of_match       = of_match_ptr("SAFEOUT"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_SAFEOUT,
-               .ops            = &max14577_safeout_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = 1,
-               .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
-               .enable_reg     = MAX14577_REG_CONTROL2,
-               .enable_mask    = CTRL2_SFOUTORD_MASK,
-       },
-       [MAX14577_CHARGER] = {
-               .name           = "CHARGER",
-               .of_match       = of_match_ptr("CHARGER"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_CHARGER,
-               .ops            = &max14577_charger_ops,
-               .type           = REGULATOR_CURRENT,
-               .owner          = THIS_MODULE,
-               .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2,
-               .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK,
-       },
+       [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+       [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
 };
 
 static struct regulator_ops max77836_ldo_ops = {
@@ -138,63 +141,28 @@ static struct regulator_ops max77836_ldo_ops = {
        /* TODO: add .set_suspend_mode */
 };
 
+#define MAX77836_LDO_REG(num)  { \
+       .name           = "LDO" # num, \
+       .of_match       = of_match_ptr("LDO" # num), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .id             = MAX77836_LDO ## num, \
+       .ops            = &max77836_ldo_ops, \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM, \
+       .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN, \
+       .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP, \
+       .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+       .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK, \
+       .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO ## num, \
+       .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK, \
+}
+
 static const struct regulator_desc max77836_supported_regulators[] = {
-       [MAX14577_SAFEOUT] = {
-               .name           = "SAFEOUT",
-               .of_match       = of_match_ptr("SAFEOUT"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_SAFEOUT,
-               .ops            = &max14577_safeout_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = 1,
-               .min_uV         = MAX14577_REGULATOR_SAFEOUT_VOLTAGE,
-               .enable_reg     = MAX14577_REG_CONTROL2,
-               .enable_mask    = CTRL2_SFOUTORD_MASK,
-       },
-       [MAX14577_CHARGER] = {
-               .name           = "CHARGER",
-               .of_match       = of_match_ptr("CHARGER"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX14577_CHARGER,
-               .ops            = &max14577_charger_ops,
-               .type           = REGULATOR_CURRENT,
-               .owner          = THIS_MODULE,
-               .enable_reg     = MAX14577_CHG_REG_CHG_CTRL2,
-               .enable_mask    = CHGCTRL2_MBCHOSTEN_MASK,
-       },
-       [MAX77836_LDO1] = {
-               .name           = "LDO1",
-               .of_match       = of_match_ptr("LDO1"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX77836_LDO1,
-               .ops            = &max77836_ldo_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
-               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
-               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
-               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO1,
-               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
-               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO1,
-               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
-       },
-       [MAX77836_LDO2] = {
-               .name           = "LDO2",
-               .of_match       = of_match_ptr("LDO2"),
-               .regulators_node = of_match_ptr("regulators"),
-               .id             = MAX77836_LDO2,
-               .ops            = &max77836_ldo_ops,
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = MAX77836_REGULATOR_LDO_VOLTAGE_STEPS_NUM,
-               .min_uV         = MAX77836_REGULATOR_LDO_VOLTAGE_MIN,
-               .uV_step        = MAX77836_REGULATOR_LDO_VOLTAGE_STEP,
-               .enable_reg     = MAX77836_LDO_REG_CNFG1_LDO2,
-               .enable_mask    = MAX77836_CNFG1_LDO_PWRMD_MASK,
-               .vsel_reg       = MAX77836_LDO_REG_CNFG1_LDO2,
-               .vsel_mask      = MAX77836_CNFG1_LDO_TV_MASK,
-       },
+       [MAX14577_SAFEOUT] = MAX14577_SAFEOUT_REG,
+       [MAX14577_CHARGER] = MAX14577_CHARGER_REG,
+       [MAX77836_LDO1] = MAX77836_LDO_REG(1),
+       [MAX77836_LDO2] = MAX77836_LDO_REG(2),
 };
 
 #ifdef CONFIG_OF
index 15fb1416bfbde99c9724644dd25bf106695fda97..17ccf365a9c04d9cb33a246f74777f316d76f037 100644 (file)
@@ -2,7 +2,7 @@
  * max77686.c - Regulator driver for the Maxim 77686
  *
  * Copyright (C) 2012 Samsung Electronics
- * Chiwoong Byun <woong.byun@smasung.com>
+ * Chiwoong Byun <woong.byun@samsung.com>
  * Jonghwa Lee <jonghwa3.lee@samsung.com>
  *
  * This program is free software; you can redistribute it and/or modify
@@ -88,7 +88,7 @@ enum max77686_ramp_rate {
 };
 
 struct max77686_data {
-       u64 gpio_enabled:MAX77686_REGULATORS;
+       DECLARE_BITMAP(gpio_enabled, MAX77686_REGULATORS);
 
        /* Array indexed by regulator id */
        unsigned int opmode[MAX77686_REGULATORS];
@@ -121,7 +121,7 @@ static unsigned int max77686_map_normal_mode(struct max77686_data *max77686,
        case MAX77686_BUCK8:
        case MAX77686_BUCK9:
        case MAX77686_LDO20 ... MAX77686_LDO22:
-               if (max77686->gpio_enabled & (1 << id))
+               if (test_bit(id, max77686->gpio_enabled))
                        return MAX77686_GPIO_CONTROL;
        }
 
@@ -277,7 +277,7 @@ static int max77686_of_parse_cb(struct device_node *np,
        }
 
        if (gpio_is_valid(config->ena_gpio)) {
-               max77686->gpio_enabled |= (1 << desc->id);
+               set_bit(desc->id, max77686->gpio_enabled);
 
                return regmap_update_bits(config->regmap, desc->enable_reg,
                                          desc->enable_mask,
index 9665a488e2f16e7ffde015d9682521b63f822f62..38722c8311a52bc04e1d4bac151fcc9bfec7d548 100644 (file)
 
 #define CHGIN_ILIM_STEP_20mA                   20000
 
-/* CHARGER regulator ops */
-/* CHARGER regulator uses two bits for enabling */
-static int max77693_chg_is_enabled(struct regulator_dev *rdev)
-{
-       int ret;
-       unsigned int val;
-
-       ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
-       if (ret)
-               return ret;
-
-       return (val & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
 /*
  * CHARGER regulator - Min : 20mA, Max : 2580mA, step : 20mA
  * 0x00, 0x01, 0x2, 0x03       = 60 mA
@@ -118,7 +104,7 @@ static struct regulator_ops max77693_safeout_ops = {
 };
 
 static struct regulator_ops max77693_charger_ops = {
-       .is_enabled             = max77693_chg_is_enabled,
+       .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .get_current_limit      = max77693_chg_get_current_limit,
@@ -155,6 +141,7 @@ static const struct regulator_desc regulators[] = {
                .enable_reg = MAX77693_CHG_REG_CHG_CNFG_00,
                .enable_mask = CHG_CNFG_00_CHG_MASK |
                                CHG_CNFG_00_BUCK_MASK,
+               .enable_val = CHG_CNFG_00_CHG_MASK | CHG_CNFG_00_BUCK_MASK,
        },
 };
 
index c132ef527cdddb307cfe9567d7eadb332739e62c..f4fd0d3cfa6ed4ea5d8e0c3fa4c38748c13968f8 100644 (file)
@@ -33,21 +33,6 @@ static const unsigned int max77843_safeout_voltage_table[] = {
        3300000,
 };
 
-static int max77843_reg_is_enabled(struct regulator_dev *rdev)
-{
-       struct regmap *regmap = rdev->regmap;
-       int ret;
-       unsigned int reg;
-
-       ret = regmap_read(regmap, rdev->desc->enable_reg, &reg);
-       if (ret) {
-               dev_err(&rdev->dev, "Fialed to read charger register\n");
-               return ret;
-       }
-
-       return (reg & rdev->desc->enable_mask) == rdev->desc->enable_mask;
-}
-
 static int max77843_reg_get_current_limit(struct regulator_dev *rdev)
 {
        struct regmap *regmap = rdev->regmap;
@@ -96,7 +81,7 @@ static int max77843_reg_set_current_limit(struct regulator_dev *rdev,
 }
 
 static struct regulator_ops max77843_charger_ops = {
-       .is_enabled             = max77843_reg_is_enabled,
+       .is_enabled             = regulator_is_enabled_regmap,
        .enable                 = regulator_enable_regmap,
        .disable                = regulator_disable_regmap,
        .get_current_limit      = max77843_reg_get_current_limit,
@@ -112,37 +97,25 @@ static struct regulator_ops max77843_regulator_ops = {
        .set_voltage_sel        = regulator_set_voltage_sel_regmap,
 };
 
+#define        MAX77843_SAFEOUT(num)   { \
+       .name           = "SAFEOUT" # num, \
+       .id             = MAX77843_SAFEOUT ## num, \
+       .ops            = &max77843_regulator_ops, \
+       .of_match       = of_match_ptr("SAFEOUT" # num), \
+       .regulators_node = of_match_ptr("regulators"), \
+       .type           = REGULATOR_VOLTAGE, \
+       .owner          = THIS_MODULE, \
+       .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table), \
+       .volt_table     = max77843_safeout_voltage_table, \
+       .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL, \
+       .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT ## num, \
+       .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL, \
+       .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT ## num ## _MASK, \
+}
+
 static const struct regulator_desc max77843_supported_regulators[] = {
-       [MAX77843_SAFEOUT1] = {
-               .name           = "SAFEOUT1",
-               .id             = MAX77843_SAFEOUT1,
-               .ops            = &max77843_regulator_ops,
-               .of_match       = of_match_ptr("SAFEOUT1"),
-               .regulators_node = of_match_ptr("regulators"),
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table),
-               .volt_table     = max77843_safeout_voltage_table,
-               .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT1,
-               .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT1_MASK,
-       },
-       [MAX77843_SAFEOUT2] = {
-               .name           = "SAFEOUT2",
-               .id             = MAX77843_SAFEOUT2,
-               .ops            = &max77843_regulator_ops,
-               .of_match       = of_match_ptr("SAFEOUT2"),
-               .regulators_node = of_match_ptr("regulators"),
-               .type           = REGULATOR_VOLTAGE,
-               .owner          = THIS_MODULE,
-               .n_voltages     = ARRAY_SIZE(max77843_safeout_voltage_table),
-               .volt_table     = max77843_safeout_voltage_table,
-               .enable_reg     = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .enable_mask    = MAX77843_REG_SAFEOUTCTRL_ENSAFEOUT2,
-               .vsel_reg       = MAX77843_SYS_REG_SAFEOUTCTRL,
-               .vsel_mask      = MAX77843_REG_SAFEOUTCTRL_SAFEOUT2_MASK,
-       },
+       [MAX77843_SAFEOUT1] = MAX77843_SAFEOUT(1),
+       [MAX77843_SAFEOUT2] = MAX77843_SAFEOUT(2),
        [MAX77843_CHARGER] = {
                .name           = "CHARGER",
                .id             = MAX77843_CHARGER,
@@ -152,7 +125,8 @@ static const struct regulator_desc max77843_supported_regulators[] = {
                .type           = REGULATOR_CURRENT,
                .owner          = THIS_MODULE,
                .enable_reg     = MAX77843_CHG_REG_CHG_CNFG_00,
-               .enable_mask    = MAX77843_CHG_MASK,
+               .enable_mask    = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
+               .enable_val     = MAX77843_CHG_MASK | MAX77843_CHG_BUCK_MASK,
        },
 };
 
index c3d55c2db593b8f37a253c4a07bd21130db99353..6f2bdad8b4d8fd6ce2e552d4c44c8cf4a3ee60ba 100644 (file)
 #include <linux/init.h>
 #include <linux/err.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 #include <linux/platform_device.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
 #include <linux/regulator/max8973-regulator.h>
 #include <linux/regulator/of_regulator.h>
 #include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #include <linux/regmap.h>
@@ -66,6 +68,7 @@
 #define MAX8973_RAMP_25mV_PER_US                       0x1
 #define MAX8973_RAMP_50mV_PER_US                       0x2
 #define MAX8973_RAMP_200mV_PER_US                      0x3
+#define MAX8973_RAMP_MASK                              0x3
 
 /* MAX8973_CONTROL2 */
 #define MAX8973_WDTMR_ENABLE                           BIT(6)
 #define MAX8973_VOLATGE_STEP                           6250
 #define MAX8973_BUCK_N_VOLTAGE                         0x80
 
+enum device_id {
+       MAX8973,
+       MAX77621
+};
+
 /* Maxim 8973 chip information */
 struct max8973_chip {
        struct device *dev;
        struct regulator_desc desc;
        struct regmap *regmap;
        bool enable_external_control;
+       int enable_gpio;
        int dvs_gpio;
        int lru_index[MAX8973_MAX_VOUT_REG];
        int curr_vout_val[MAX8973_MAX_VOUT_REG];
        int curr_vout_reg;
        int curr_gpio_val;
-       bool valid_dvs_gpio;
        struct regulator_ops ops;
+       enum device_id id;
 };
 
 /*
@@ -174,7 +183,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
         * If gpios are available to select the VOUT register then least
         * recently used register for new configuration.
         */
-       if (max->valid_dvs_gpio)
+       if (gpio_is_valid(max->dvs_gpio))
                found = find_voltage_set_register(max, vsel,
                                        &vout_reg, &gpio_val);
 
@@ -191,7 +200,7 @@ static int max8973_dcdc_set_voltage_sel(struct regulator_dev *rdev,
        }
 
        /* Select proper VOUT register vio gpios */
-       if (max->valid_dvs_gpio) {
+       if (gpio_is_valid(max->dvs_gpio)) {
                gpio_set_value_cansleep(max->dvs_gpio, gpio_val & 0x1);
                max->curr_gpio_val = gpio_val;
        }
@@ -242,12 +251,45 @@ static unsigned int max8973_dcdc_get_mode(struct regulator_dev *rdev)
                REGULATOR_MODE_FAST : REGULATOR_MODE_NORMAL;
 }
 
+static int max8973_set_ramp_delay(struct regulator_dev *rdev,
+               int ramp_delay)
+{
+       struct max8973_chip *max = rdev_get_drvdata(rdev);
+       unsigned int control;
+       int ret;
+       int ret_val;
+
+       /* Set ramp delay */
+       if (ramp_delay < 25000) {
+               control = MAX8973_RAMP_12mV_PER_US;
+               ret_val = 12000;
+       } else if (ramp_delay < 50000) {
+               control = MAX8973_RAMP_25mV_PER_US;
+               ret_val = 25000;
+       } else if (ramp_delay < 200000) {
+               control = MAX8973_RAMP_50mV_PER_US;
+               ret_val = 50000;
+       } else {
+               control = MAX8973_RAMP_200mV_PER_US;
+               ret_val = 200000;
+       }
+
+       ret = regmap_update_bits(max->regmap, MAX8973_CONTROL1,
+                       MAX8973_RAMP_MASK, control);
+       if (ret < 0)
+               dev_err(max->dev, "register %d update failed, %d",
+                               MAX8973_CONTROL1, ret);
+       return ret;
+}
+
 static const struct regulator_ops max8973_dcdc_ops = {
        .get_voltage_sel        = max8973_dcdc_get_voltage_sel,
        .set_voltage_sel        = max8973_dcdc_set_voltage_sel,
        .list_voltage           = regulator_list_voltage_linear,
        .set_mode               = max8973_dcdc_set_mode,
        .get_mode               = max8973_dcdc_get_mode,
+       .set_voltage_time_sel   = regulator_set_voltage_time_sel,
+       .set_ramp_delay         = max8973_set_ramp_delay,
 };
 
 static int max8973_init_dcdc(struct max8973_chip *max,
@@ -256,6 +298,29 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        int ret;
        uint8_t control1 = 0;
        uint8_t control2 = 0;
+       unsigned int data;
+
+       ret = regmap_read(max->regmap, MAX8973_CONTROL1, &data);
+       if (ret < 0) {
+               dev_err(max->dev, "register %d read failed, err = %d",
+                               MAX8973_CONTROL1, ret);
+               return ret;
+       }
+       control1 = data & MAX8973_RAMP_MASK;
+       switch (control1) {
+       case MAX8973_RAMP_12mV_PER_US:
+               max->desc.ramp_delay = 12000;
+               break;
+       case MAX8973_RAMP_25mV_PER_US:
+               max->desc.ramp_delay = 25000;
+               break;
+       case MAX8973_RAMP_50mV_PER_US:
+               max->desc.ramp_delay = 50000;
+               break;
+       case MAX8973_RAMP_200mV_PER_US:
+               max->desc.ramp_delay = 200000;
+               break;
+       }
 
        if (pdata->control_flags & MAX8973_CONTROL_REMOTE_SENSE_ENABLE)
                control1 |= MAX8973_SNS_ENABLE;
@@ -266,28 +331,16 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        if (pdata->control_flags & MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE)
                control1 |= MAX8973_AD_ENABLE;
 
-       if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE)
+       if (pdata->control_flags & MAX8973_CONTROL_BIAS_ENABLE) {
                control1 |= MAX8973_BIAS_ENABLE;
+               max->desc.enable_time = 20;
+       } else {
+               max->desc.enable_time = 240;
+       }
 
        if (pdata->control_flags & MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE)
                control1 |= MAX8973_FREQSHIFT_9PER;
 
-       /* Set ramp delay */
-       if (pdata->reg_init_data &&
-                       pdata->reg_init_data->constraints.ramp_delay) {
-               if (pdata->reg_init_data->constraints.ramp_delay < 25000)
-                       control1 |= MAX8973_RAMP_12mV_PER_US;
-               else if (pdata->reg_init_data->constraints.ramp_delay < 50000)
-                       control1 |= MAX8973_RAMP_25mV_PER_US;
-               else if (pdata->reg_init_data->constraints.ramp_delay < 200000)
-                       control1 |= MAX8973_RAMP_50mV_PER_US;
-               else
-                       control1 |= MAX8973_RAMP_200mV_PER_US;
-       } else {
-               control1 |= MAX8973_RAMP_12mV_PER_US;
-               max->desc.ramp_delay = 12500;
-       }
-
        if (!(pdata->control_flags & MAX8973_CONTROL_PULL_DOWN_ENABLE))
                control2 |= MAX8973_DISCH_ENBABLE;
 
@@ -344,7 +397,7 @@ static int max8973_init_dcdc(struct max8973_chip *max,
        }
 
        /* If external control is enabled then disable EN bit */
-       if (max->enable_external_control) {
+       if (max->enable_external_control && (max->id == MAX8973)) {
                ret = regmap_update_bits(max->regmap, MAX8973_VOUT,
                                                MAX8973_VOUT_ENABLE, 0);
                if (ret < 0)
@@ -361,22 +414,82 @@ static const struct regmap_config max8973_regmap_config = {
        .cache_type             = REGCACHE_RBTREE,
 };
 
+static struct max8973_regulator_platform_data *max8973_parse_dt(
+               struct device *dev)
+{
+       struct max8973_regulator_platform_data *pdata;
+       struct device_node *np = dev->of_node;
+       int ret;
+       u32 pval;
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       pdata->enable_ext_control = of_property_read_bool(np,
+                                               "maxim,externally-enable");
+       pdata->enable_gpio = of_get_named_gpio(np, "maxim,enable-gpio", 0);
+       pdata->dvs_gpio = of_get_named_gpio(np, "maxim,dvs-gpio", 0);
+
+       ret = of_property_read_u32(np, "maxim,dvs-default-state", &pval);
+       if (!ret)
+               pdata->dvs_def_state = pval;
+
+       if (of_property_read_bool(np, "maxim,enable-remote-sense"))
+               pdata->control_flags  |= MAX8973_CONTROL_REMOTE_SENSE_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-falling-slew-rate"))
+               pdata->control_flags  |=
+                               MAX8973_CONTROL_FALLING_SLEW_RATE_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-active-discharge"))
+               pdata->control_flags  |=
+                               MAX8973_CONTROL_OUTPUT_ACTIVE_DISCH_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-frequency-shift"))
+               pdata->control_flags  |= MAX8973_CONTROL_FREQ_SHIFT_9PER_ENABLE;
+
+       if (of_property_read_bool(np, "maxim,enable-bias-control"))
+               pdata->control_flags  |= MAX8973_BIAS_ENABLE;
+
+       return pdata;
+}
+
+static const struct of_device_id of_max8973_match_tbl[] = {
+       { .compatible = "maxim,max8973", .data = (void *)MAX8973, },
+       { .compatible = "maxim,max77621", .data = (void *)MAX77621, },
+       { },
+};
+MODULE_DEVICE_TABLE(of, of_max8973_match_tbl);
+
 static int max8973_probe(struct i2c_client *client,
                         const struct i2c_device_id *id)
 {
        struct max8973_regulator_platform_data *pdata;
+       struct regulator_init_data *ridata;
        struct regulator_config config = { };
        struct regulator_dev *rdev;
        struct max8973_chip *max;
+       bool pdata_from_dt = false;
+       unsigned int chip_id;
        int ret;
 
        pdata = dev_get_platdata(&client->dev);
 
-       if (!pdata && !client->dev.of_node) {
+       if (!pdata && client->dev.of_node) {
+               pdata = max8973_parse_dt(&client->dev);
+               pdata_from_dt = true;
+       }
+
+       if (!pdata) {
                dev_err(&client->dev, "No Platform data");
                return -EIO;
        }
 
+       if ((pdata->dvs_gpio == -EPROBE_DEFER) ||
+               (pdata->enable_gpio == -EPROBE_DEFER))
+               return -EPROBE_DEFER;
+
        max = devm_kzalloc(&client->dev, sizeof(*max), GFP_KERNEL);
        if (!max)
                return -ENOMEM;
@@ -388,6 +501,27 @@ static int max8973_probe(struct i2c_client *client,
                return ret;
        }
 
+       if (client->dev.of_node) {
+               const struct of_device_id *match;
+
+               match = of_match_device(of_match_ptr(of_max8973_match_tbl),
+                               &client->dev);
+               if (!match)
+                       return -ENODATA;
+               max->id = (u32)((uintptr_t)match->data);
+       } else {
+               max->id = id->driver_data;
+       }
+
+       ret = regmap_read(max->regmap, MAX8973_CHIPID1, &chip_id);
+       if (ret < 0) {
+               dev_err(&client->dev, "register CHIPID1 read failed, %d", ret);
+               return ret;
+       }
+
+       dev_info(&client->dev, "CHIP-ID OTP: 0x%02x ID_M: 0x%02x\n",
+                       (chip_id >> 4) & 0xF, (chip_id >> 1) & 0x7);
+
        i2c_set_clientdata(client, max);
        max->ops = max8973_dcdc_ops;
        max->dev = &client->dev;
@@ -400,23 +534,14 @@ static int max8973_probe(struct i2c_client *client,
        max->desc.uV_step = MAX8973_VOLATGE_STEP;
        max->desc.n_voltages = MAX8973_BUCK_N_VOLTAGE;
 
-       if (!pdata || !pdata->enable_ext_control) {
-               max->desc.enable_reg = MAX8973_VOUT;
-               max->desc.enable_mask = MAX8973_VOUT_ENABLE;
-               max->ops.enable = regulator_enable_regmap;
-               max->ops.disable = regulator_disable_regmap;
-               max->ops.is_enabled = regulator_is_enabled_regmap;
-       }
+       max->dvs_gpio = (pdata->dvs_gpio) ? pdata->dvs_gpio : -EINVAL;
+       max->enable_gpio = (pdata->enable_gpio) ? pdata->enable_gpio : -EINVAL;
+       max->enable_external_control = pdata->enable_ext_control;
+       max->curr_gpio_val = pdata->dvs_def_state;
+       max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
 
-       if (pdata) {
-               max->dvs_gpio = pdata->dvs_gpio;
-               max->enable_external_control = pdata->enable_ext_control;
-               max->curr_gpio_val = pdata->dvs_def_state;
-               max->curr_vout_reg = MAX8973_VOUT + pdata->dvs_def_state;
-       } else {
-               max->dvs_gpio = -EINVAL;
-               max->curr_vout_reg = MAX8973_VOUT;
-       }
+       if (gpio_is_valid(max->enable_gpio))
+               max->enable_external_control = true;
 
        max->lru_index[0] = max->curr_vout_reg;
 
@@ -434,7 +559,6 @@ static int max8973_probe(struct i2c_client *client,
                                max->dvs_gpio, ret);
                        return ret;
                }
-               max->valid_dvs_gpio = true;
 
                /*
                 * Initialize the lru index with vout_reg id
@@ -444,22 +568,64 @@ static int max8973_probe(struct i2c_client *client,
                        max->lru_index[i] = i;
                max->lru_index[0] = max->curr_vout_reg;
                max->lru_index[max->curr_vout_reg] = 0;
-       } else {
-               max->valid_dvs_gpio = false;
        }
 
-       if (pdata) {
-               ret = max8973_init_dcdc(max, pdata);
-               if (ret < 0) {
-                       dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
-                       return ret;
+       if (pdata_from_dt)
+               pdata->reg_init_data = of_get_regulator_init_data(&client->dev,
+                                       client->dev.of_node, &max->desc);
+
+       ridata = pdata->reg_init_data;
+       switch (max->id) {
+       case MAX8973:
+               if (!pdata->enable_ext_control) {
+                       max->desc.enable_reg = MAX8973_VOUT;
+                       max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+                       max->ops.enable = regulator_enable_regmap;
+                       max->ops.disable = regulator_disable_regmap;
+                       max->ops.is_enabled = regulator_is_enabled_regmap;
+                       break;
+               }
+
+               if (gpio_is_valid(max->enable_gpio)) {
+                       config.ena_gpio_flags = GPIOF_OUT_INIT_LOW;
+                       if (ridata && (ridata->constraints.always_on ||
+                                       ridata->constraints.boot_on))
+                               config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH;
+                       config.ena_gpio = max->enable_gpio;
                }
+               break;
+
+       case MAX77621:
+               if (gpio_is_valid(max->enable_gpio)) {
+                       ret = devm_gpio_request_one(&client->dev,
+                                       max->enable_gpio, GPIOF_OUT_INIT_HIGH,
+                                       "max8973-en-gpio");
+                       if (ret) {
+                               dev_err(&client->dev,
+                                       "gpio_request for gpio %d failed: %d\n",
+                                       max->enable_gpio, ret);
+                               return ret;
+                       }
+               }
+
+               max->desc.enable_reg = MAX8973_VOUT;
+               max->desc.enable_mask = MAX8973_VOUT_ENABLE;
+               max->ops.enable = regulator_enable_regmap;
+               max->ops.disable = regulator_disable_regmap;
+               max->ops.is_enabled = regulator_is_enabled_regmap;
+               break;
+       default:
+               break;
+       }
+
+       ret = max8973_init_dcdc(max, pdata);
+       if (ret < 0) {
+               dev_err(max->dev, "Max8973 Init failed, err = %d\n", ret);
+               return ret;
        }
 
        config.dev = &client->dev;
-       config.init_data = pdata ? pdata->reg_init_data :
-               of_get_regulator_init_data(&client->dev, client->dev.of_node,
-                                          &max->desc);
+       config.init_data = pdata->reg_init_data;
        config.driver_data = max;
        config.of_node = client->dev.of_node;
        config.regmap = max->regmap;
@@ -476,15 +642,16 @@ static int max8973_probe(struct i2c_client *client,
 }
 
 static const struct i2c_device_id max8973_id[] = {
-       {.name = "max8973",},
+       {.name = "max8973", .driver_data = MAX8973},
+       {.name = "max77621", .driver_data = MAX77621},
        {},
 };
-
 MODULE_DEVICE_TABLE(i2c, max8973_id);
 
 static struct i2c_driver max8973_i2c_driver = {
        .driver = {
                .name = "max8973",
+               .of_match_table = of_max8973_match_tbl,
                .owner = THIS_MODULE,
        },
        .probe = max8973_probe,
index 24e812c48d93076a36039e991c51bb371fb26d6e..b1c485b24ab246234edbab2dfb4217e4fe1df833 100644 (file)
@@ -58,6 +58,10 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!of_property_read_u32(np, "regulator-max-microamp", &pval))
                constraints->max_uA = pval;
 
+       if (!of_property_read_u32(np, "regulator-input-current-limit-microamp",
+                                 &pval))
+               constraints->ilim_uA = pval;
+
        /* Current change possible? */
        if (constraints->min_uA != constraints->max_uA)
                constraints->valid_ops_mask |= REGULATOR_CHANGE_CURRENT;
@@ -67,6 +71,8 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!constraints->always_on) /* status change should be possible. */
                constraints->valid_ops_mask |= REGULATOR_CHANGE_STATUS;
 
+       constraints->pull_down = of_property_read_bool(np, "regulator-pull-down");
+
        if (of_property_read_bool(np, "regulator-allow-bypass"))
                constraints->valid_ops_mask |= REGULATOR_CHANGE_BYPASS;
 
@@ -82,6 +88,9 @@ static void of_get_regulation_constraints(struct device_node *np,
        if (!ret)
                constraints->enable_time = pval;
 
+       constraints->soft_start = of_property_read_bool(np,
+                                       "regulator-soft-start");
+
        if (!of_property_read_u32(np, "regulator-initial-mode", &pval)) {
                if (desc && desc->of_map_mode) {
                        ret = desc->of_map_mode(pval);
@@ -95,6 +104,9 @@ static void of_get_regulation_constraints(struct device_node *np,
                }
        }
 
+       if (!of_property_read_u32(np, "regulator-system-load", &pval))
+               constraints->system_load = pval;
+
        for (i = 0; i < ARRAY_SIZE(regulator_states); i++) {
                switch (i) {
                case PM_SUSPEND_MEM:
@@ -108,7 +120,7 @@ static void of_get_regulation_constraints(struct device_node *np,
                case PM_SUSPEND_STANDBY:
                default:
                        continue;
-               };
+               }
 
                suspend_np = of_get_child_by_name(np, regulator_states[i]);
                if (!suspend_np || !suspend_state)
@@ -292,7 +304,7 @@ struct regulator_init_data *regulator_of_get_init_data(struct device *dev,
                return NULL;
        }
 
-       for_each_child_of_node(search, child) {
+       for_each_available_child_of_node(search, child) {
                name = of_get_property(child, "regulator-compatible", NULL);
                if (!name)
                        name = child->name;
index 253833ae35f357602c680618c0a0b54315963337..ffa96124a5e7206f4f86f7025098a3744f9ec315 100644 (file)
 #include <linux/pwm.h>
 
 struct pwm_regulator_data {
-       struct regulator_desc desc;
        struct pwm_voltages *duty_cycle_table;
        struct pwm_device *pwm;
-       bool enabled;
        int state;
 };
 
@@ -33,17 +31,17 @@ struct pwm_voltages {
        unsigned int dutycycle;
 };
 
-static int pwm_regulator_get_voltage_sel(struct regulator_dev *dev)
+static int pwm_regulator_get_voltage_sel(struct regulator_dev *rdev)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
 
        return drvdata->state;
 }
 
-static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
+static int pwm_regulator_set_voltage_sel(struct regulator_dev *rdev,
                                         unsigned selector)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
        unsigned int pwm_reg_period;
        int dutycycle;
        int ret;
@@ -55,30 +53,27 @@ static int pwm_regulator_set_voltage_sel(struct regulator_dev *dev,
 
        ret = pwm_config(drvdata->pwm, dutycycle, pwm_reg_period);
        if (ret) {
-               dev_err(&dev->dev, "Failed to configure PWM\n");
+               dev_err(&rdev->dev, "Failed to configure PWM\n");
                return ret;
        }
 
        drvdata->state = selector;
 
-       if (!drvdata->enabled) {
-               ret = pwm_enable(drvdata->pwm);
-               if (ret) {
-                       dev_err(&dev->dev, "Failed to enable PWM\n");
-                       return ret;
-               }
-               drvdata->enabled = true;
+       ret = pwm_enable(drvdata->pwm);
+       if (ret) {
+               dev_err(&rdev->dev, "Failed to enable PWM\n");
+               return ret;
        }
 
        return 0;
 }
 
-static int pwm_regulator_list_voltage(struct regulator_dev *dev,
+static int pwm_regulator_list_voltage(struct regulator_dev *rdev,
                                      unsigned selector)
 {
-       struct pwm_regulator_data *drvdata = rdev_get_drvdata(dev);
+       struct pwm_regulator_data *drvdata = rdev_get_drvdata(rdev);
 
-       if (selector >= drvdata->desc.n_voltages)
+       if (selector >= rdev->desc->n_voltages)
                return -EINVAL;
 
        return drvdata->duty_cycle_table[selector].uV;
@@ -91,7 +86,7 @@ static struct regulator_ops pwm_regulator_voltage_ops = {
        .map_voltage     = regulator_map_voltage_iterate,
 };
 
-static const struct regulator_desc pwm_regulator_desc = {
+static struct regulator_desc pwm_regulator_desc = {
        .name           = "pwm-regulator",
        .ops            = &pwm_regulator_voltage_ops,
        .type           = REGULATOR_VOLTAGE,
@@ -117,8 +112,6 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        if (!drvdata)
                return -ENOMEM;
 
-       memcpy(&drvdata->desc, &pwm_regulator_desc, sizeof(pwm_regulator_desc));
-
        /* determine the number of voltage-table */
        prop = of_find_property(np, "voltage-table", &length);
        if (!prop) {
@@ -133,7 +126,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       drvdata->desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
+       pwm_regulator_desc.n_voltages = length / sizeof(*drvdata->duty_cycle_table);
 
        drvdata->duty_cycle_table = devm_kzalloc(&pdev->dev,
                                                 length, GFP_KERNEL);
@@ -150,7 +143,7 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        }
 
        config.init_data = of_get_regulator_init_data(&pdev->dev, np,
-                                                     &drvdata->desc);
+                                                     &pwm_regulator_desc);
        if (!config.init_data)
                return -ENOMEM;
 
@@ -165,10 +158,10 @@ static int pwm_regulator_probe(struct platform_device *pdev)
        }
 
        regulator = devm_regulator_register(&pdev->dev,
-                                           &drvdata->desc, &config);
+                                           &pwm_regulator_desc, &config);
        if (IS_ERR(regulator)) {
                dev_err(&pdev->dev, "Failed to register regulator %s\n",
-                       drvdata->desc.name);
+                       pwm_regulator_desc.name);
                return PTR_ERR(regulator);
        }
 
diff --git a/drivers/regulator/qcom_spmi-regulator.c b/drivers/regulator/qcom_spmi-regulator.c
new file mode 100644 (file)
index 0000000..850a30a
--- /dev/null
@@ -0,0 +1,1435 @@
+/*
+ * Copyright (c) 2012-2015, The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/err.h>
+#include <linux/kernel.h>
+#include <linux/interrupt.h>
+#include <linux/bitops.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/platform_device.h>
+#include <linux/ktime.h>
+#include <linux/regulator/driver.h>
+#include <linux/regmap.h>
+#include <linux/list.h>
+
+/* These types correspond to unique register layouts. */
+enum spmi_regulator_logical_type {
+       SPMI_REGULATOR_LOGICAL_TYPE_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_VS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST,
+       SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_BOOST_BYP,
+       SPMI_REGULATOR_LOGICAL_TYPE_LN_LDO,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_HO_SMPS,
+       SPMI_REGULATOR_LOGICAL_TYPE_ULT_LDO,
+};
+
+enum spmi_regulator_type {
+       SPMI_REGULATOR_TYPE_BUCK                = 0x03,
+       SPMI_REGULATOR_TYPE_LDO                 = 0x04,
+       SPMI_REGULATOR_TYPE_VS                  = 0x05,
+       SPMI_REGULATOR_TYPE_BOOST               = 0x1b,
+       SPMI_REGULATOR_TYPE_FTS                 = 0x1c,
+       SPMI_REGULATOR_TYPE_BOOST_BYP           = 0x1f,
+       SPMI_REGULATOR_TYPE_ULT_LDO             = 0x21,
+       SPMI_REGULATOR_TYPE_ULT_BUCK            = 0x22,
+};
+
+enum spmi_regulator_subtype {
+       SPMI_REGULATOR_SUBTYPE_GP_CTL           = 0x08,
+       SPMI_REGULATOR_SUBTYPE_RF_CTL           = 0x09,
+       SPMI_REGULATOR_SUBTYPE_N50              = 0x01,
+       SPMI_REGULATOR_SUBTYPE_N150             = 0x02,
+       SPMI_REGULATOR_SUBTYPE_N300             = 0x03,
+       SPMI_REGULATOR_SUBTYPE_N600             = 0x04,
+       SPMI_REGULATOR_SUBTYPE_N1200            = 0x05,
+       SPMI_REGULATOR_SUBTYPE_N600_ST          = 0x06,
+       SPMI_REGULATOR_SUBTYPE_N1200_ST         = 0x07,
+       SPMI_REGULATOR_SUBTYPE_N900_ST          = 0x14,
+       SPMI_REGULATOR_SUBTYPE_N300_ST          = 0x15,
+       SPMI_REGULATOR_SUBTYPE_P50              = 0x08,
+       SPMI_REGULATOR_SUBTYPE_P150             = 0x09,
+       SPMI_REGULATOR_SUBTYPE_P300             = 0x0a,
+       SPMI_REGULATOR_SUBTYPE_P600             = 0x0b,
+       SPMI_REGULATOR_SUBTYPE_P1200            = 0x0c,
+       SPMI_REGULATOR_SUBTYPE_LN               = 0x10,
+       SPMI_REGULATOR_SUBTYPE_LV_P50           = 0x28,
+       SPMI_REGULATOR_SUBTYPE_LV_P150          = 0x29,
+       SPMI_REGULATOR_SUBTYPE_LV_P300          = 0x2a,
+       SPMI_REGULATOR_SUBTYPE_LV_P600          = 0x2b,
+       SPMI_REGULATOR_SUBTYPE_LV_P1200         = 0x2c,
+       SPMI_REGULATOR_SUBTYPE_LV_P450          = 0x2d,
+       SPMI_REGULATOR_SUBTYPE_LV100            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_LV300            = 0x02,
+       SPMI_REGULATOR_SUBTYPE_MV300            = 0x08,
+       SPMI_REGULATOR_SUBTYPE_MV500            = 0x09,
+       SPMI_REGULATOR_SUBTYPE_HDMI             = 0x10,
+       SPMI_REGULATOR_SUBTYPE_OTG              = 0x11,
+       SPMI_REGULATOR_SUBTYPE_5V_BOOST         = 0x01,
+       SPMI_REGULATOR_SUBTYPE_FTS_CTL          = 0x08,
+       SPMI_REGULATOR_SUBTYPE_FTS2p5_CTL       = 0x09,
+       SPMI_REGULATOR_SUBTYPE_BB_2A            = 0x01,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL1      = 0x0d,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL2      = 0x0e,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL3      = 0x0f,
+       SPMI_REGULATOR_SUBTYPE_ULT_HF_CTL4      = 0x10,
+};
+
+enum spmi_common_regulator_registers {
+       SPMI_COMMON_REG_DIG_MAJOR_REV           = 0x01,
+       SPMI_COMMON_REG_TYPE                    = 0x04,
+       SPMI_COMMON_REG_SUBTYPE                 = 0x05,
+       SPMI_COMMON_REG_VOLTAGE_RANGE           = 0x40,
+       SPMI_COMMON_REG_VOLTAGE_SET             = 0x41,
+       SPMI_COMMON_REG_MODE                    = 0x45,
+       SPMI_COMMON_REG_ENABLE                  = 0x46,
+       SPMI_COMMON_REG_PULL_DOWN               = 0x48,
+       SPMI_COMMON_REG_SOFT_START              = 0x4c,
+       SPMI_COMMON_REG_STEP_CTRL               = 0x61,
+};
+
+enum spmi_vs_registers {
+       SPMI_VS_REG_OCP                         = 0x4a,
+       SPMI_VS_REG_SOFT_START                  = 0x4c,
+};
+
+enum spmi_boost_registers {
+       SPMI_BOOST_REG_CURRENT_LIMIT            = 0x4a,
+};
+
+enum spmi_boost_byp_registers {
+       SPMI_BOOST_BYP_REG_CURRENT_LIMIT        = 0x4b,
+};
+
+/* Used for indexing into ctrl_reg.  These are offets from 0x40 */
+enum spmi_common_control_register_index {
+       SPMI_COMMON_IDX_VOLTAGE_RANGE           = 0,
+       SPMI_COMMON_IDX_VOLTAGE_SET             = 1,
+       SPMI_COMMON_IDX_MODE                    = 5,
+       SPMI_COMMON_IDX_ENABLE                  = 6,
+};
+
+/* Common regulator control register layout */
+#define SPMI_COMMON_ENABLE_MASK                        0x80
+#define SPMI_COMMON_ENABLE                     0x80
+#define SPMI_COMMON_DISABLE                    0x00
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN3_MASK  0x08
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN2_MASK  0x04
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN1_MASK  0x02
+#define SPMI_COMMON_ENABLE_FOLLOW_HW_EN0_MASK  0x01
+#define SPMI_COMMON_ENABLE_FOLLOW_ALL_MASK     0x0f
+
+/* Common regulator mode register layout */
+#define SPMI_COMMON_MODE_HPM_MASK              0x80
+#define SPMI_COMMON_MODE_AUTO_MASK             0x40
+#define SPMI_COMMON_MODE_BYPASS_MASK           0x20
+#define SPMI_COMMON_MODE_FOLLOW_AWAKE_MASK     0x10
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN3_MASK    0x08
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN2_MASK    0x04
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN1_MASK    0x02
+#define SPMI_COMMON_MODE_FOLLOW_HW_EN0_MASK    0x01
+#define SPMI_COMMON_MODE_FOLLOW_ALL_MASK       0x1f
+
+/* Common regulator pull down control register layout */
+#define SPMI_COMMON_PULL_DOWN_ENABLE_MASK      0x80
+
+/* LDO regulator current limit control register layout */
+#define SPMI_LDO_CURRENT_LIMIT_ENABLE_MASK     0x80
+
+/* LDO regulator soft start control register layout */
+#define SPMI_LDO_SOFT_START_ENABLE_MASK                0x80
+
+/* VS regulator over current protection control register layout */
+#define SPMI_VS_OCP_OVERRIDE                   0x01
+#define SPMI_VS_OCP_NO_OVERRIDE                        0x00
+
+/* VS regulator soft start control register layout */
+#define SPMI_VS_SOFT_START_ENABLE_MASK         0x80
+#define SPMI_VS_SOFT_START_SEL_MASK            0x03
+
+/* Boost regulator current limit control register layout */
+#define SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK   0x80
+#define SPMI_BOOST_CURRENT_LIMIT_MASK          0x07
+
+#define SPMI_VS_OCP_DEFAULT_MAX_RETRIES                10
+#define SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS     30
+#define SPMI_VS_OCP_FALL_DELAY_US              90
+#define SPMI_VS_OCP_FAULT_DELAY_US             20000
+
+#define SPMI_FTSMPS_STEP_CTRL_STEP_MASK                0x18
+#define SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT       3
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_MASK       0x07
+#define SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT      0
+
+/* Clock rate in kHz of the FTSMPS regulator reference clock. */
+#define SPMI_FTSMPS_CLOCK_RATE         19200
+
+/* Minimum voltage stepper delay for each step. */
+#define SPMI_FTSMPS_STEP_DELAY         8
+
+/*
+ * The ratio SPMI_FTSMPS_STEP_MARGIN_NUM/SPMI_FTSMPS_STEP_MARGIN_DEN is used to
+ * adjust the step rate in order to account for oscillator variance.
+ */
+#define SPMI_FTSMPS_STEP_MARGIN_NUM    4
+#define SPMI_FTSMPS_STEP_MARGIN_DEN    5
+
+/*
+ * This voltage in uV is returned by get_voltage functions when there is no way
+ * to determine the current voltage level.  It is needed because the regulator
+ * framework treats a 0 uV voltage as an error.
+ */
+#define VOLTAGE_UNKNOWN 1
+
+/* VSET value to decide the range of ULT SMPS */
+#define ULT_SMPS_RANGE_SPLIT 0x60
+
+/**
+ * struct spmi_voltage_range - regulator set point voltage mapping description
+ * @min_uV:            Minimum programmable output voltage resulting from
+ *                     set point register value 0x00
+ * @max_uV:            Maximum programmable output voltage
+ * @step_uV:           Output voltage increase resulting from the set point
+ *                     register value increasing by 1
+ * @set_point_min_uV:  Minimum allowed voltage
+ * @set_point_max_uV:  Maximum allowed voltage.  This may be tweaked in order
+ *                     to pick which range should be used in the case of
+ *                     overlapping set points.
+ * @n_voltages:                Number of preferred voltage set points present in this
+ *                     range
+ * @range_sel:         Voltage range register value corresponding to this range
+ *
+ * The following relationships must be true for the values used in this struct:
+ * (max_uV - min_uV) % step_uV == 0
+ * (set_point_min_uV - min_uV) % step_uV == 0*
+ * (set_point_max_uV - min_uV) % step_uV == 0*
+ * n_voltages = (set_point_max_uV - set_point_min_uV) / step_uV + 1
+ *
+ * *Note, set_point_min_uV == set_point_max_uV == 0 is allowed in order to
+ * specify that the voltage range has meaning, but is not preferred.
+ */
+struct spmi_voltage_range {
+       int                                     min_uV;
+       int                                     max_uV;
+       int                                     step_uV;
+       int                                     set_point_min_uV;
+       int                                     set_point_max_uV;
+       unsigned                                n_voltages;
+       u8                                      range_sel;
+};
+
+/*
+ * The ranges specified in the spmi_voltage_set_points struct must be listed
+ * so that range[i].set_point_max_uV < range[i+1].set_point_min_uV.
+ */
+struct spmi_voltage_set_points {
+       struct spmi_voltage_range               *range;
+       int                                     count;
+       unsigned                                n_voltages;
+};
+
+struct spmi_regulator {
+       struct regulator_desc                   desc;
+       struct device                           *dev;
+       struct delayed_work                     ocp_work;
+       struct regmap                           *regmap;
+       struct spmi_voltage_set_points          *set_points;
+       enum spmi_regulator_logical_type        logical_type;
+       int                                     ocp_irq;
+       int                                     ocp_count;
+       int                                     ocp_max_retries;
+       int                                     ocp_retry_delay_ms;
+       int                                     hpm_min_load;
+       int                                     slew_rate;
+       ktime_t                                 vs_enable_time;
+       u16                                     base;
+       struct list_head                        node;
+};
+
+struct spmi_regulator_mapping {
+       enum spmi_regulator_type                type;
+       enum spmi_regulator_subtype             subtype;
+       enum spmi_regulator_logical_type        logical_type;
+       u32                                     revision_min;
+       u32                                     revision_max;
+       struct regulator_ops                    *ops;
+       struct spmi_voltage_set_points          *set_points;
+       int                                     hpm_min_load;
+};
+
+struct spmi_regulator_data {
+       const char                      *name;
+       u16                             base;
+       const char                      *supply;
+       const char                      *ocp;
+       u16                             force_type;
+};
+
+#define SPMI_VREG(_type, _subtype, _dig_major_min, _dig_major_max, \
+                     _logical_type, _ops_val, _set_points_val, _hpm_min_load) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_##_type, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_##_logical_type, \
+               .ops            = &spmi_##_ops_val##_ops, \
+               .set_points     = &_set_points_val##_set_points, \
+               .hpm_min_load   = _hpm_min_load, \
+       }
+
+#define SPMI_VREG_VS(_subtype, _dig_major_min, _dig_major_max) \
+       { \
+               .type           = SPMI_REGULATOR_TYPE_VS, \
+               .subtype        = SPMI_REGULATOR_SUBTYPE_##_subtype, \
+               .revision_min   = _dig_major_min, \
+               .revision_max   = _dig_major_max, \
+               .logical_type   = SPMI_REGULATOR_LOGICAL_TYPE_VS, \
+               .ops            = &spmi_vs_ops, \
+       }
+
+#define SPMI_VOLTAGE_RANGE(_range_sel, _min_uV, _set_point_min_uV, \
+                       _set_point_max_uV, _max_uV, _step_uV) \
+       { \
+               .min_uV                 = _min_uV, \
+               .max_uV                 = _max_uV, \
+               .set_point_min_uV       = _set_point_min_uV, \
+               .set_point_max_uV       = _set_point_max_uV, \
+               .step_uV                = _step_uV, \
+               .range_sel              = _range_sel, \
+       }
+
+#define DEFINE_SPMI_SET_POINTS(name) \
+struct spmi_voltage_set_points name##_set_points = { \
+       .range  = name##_ranges, \
+       .count  = ARRAY_SIZE(name##_ranges), \
+}
+
+/*
+ * These tables contain the physically available PMIC regulator voltage setpoint
+ * ranges.  Where two ranges overlap in hardware, one of the ranges is trimmed
+ * to ensure that the setpoints available to software are monotonically
+ * increasing and unique.  The set_voltage callback functions expect these
+ * properties to hold.
+ */
+static struct spmi_voltage_range pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(3, 1500000, 1550000, 3075000, 3075000, 25000),
+       SPMI_VOLTAGE_RANGE(4, 1750000, 3100000, 4900000, 4900000, 50000),
+};
+
+static struct spmi_voltage_range nldo1_ranges[] = {
+       SPMI_VOLTAGE_RANGE(2,  750000,  750000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo2_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,  375000,  768750,  768750,  6250),
+       SPMI_VOLTAGE_RANGE(2,  750000,  775000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range nldo3_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  375000,       0,       0, 1537500, 12500),
+       SPMI_VOLTAGE_RANGE(2,  750000,       0,       0, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ln_ldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(1,  690000,  690000, 1110000, 1110000, 60000),
+       SPMI_VOLTAGE_RANGE(0, 1380000, 1380000, 2220000, 2220000, 120000),
+};
+
+static struct spmi_voltage_range smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1, 1550000, 1575000, 3125000, 3125000, 25000),
+};
+
+static struct spmi_voltage_range ftsmps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,       0,  350000, 1275000, 1275000,  5000),
+       SPMI_VOLTAGE_RANGE(1,       0, 1280000, 2040000, 2040000, 10000),
+};
+
+static struct spmi_voltage_range ftsmps2p5_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,   80000,  350000, 1355000, 1355000,  5000),
+       SPMI_VOLTAGE_RANGE(1,  160000, 1360000, 2200000, 2200000, 10000),
+};
+
+static struct spmi_voltage_range boost_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 4000000, 4000000, 5550000, 5550000, 50000),
+};
+
+static struct spmi_voltage_range boost_byp_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 2500000, 2500000, 5200000, 5650000, 50000),
+};
+
+static struct spmi_voltage_range ult_lo_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1562500, 1562500, 12500),
+       SPMI_VOLTAGE_RANGE(1,  750000,       0,       0, 1525000, 25000),
+};
+
+static struct spmi_voltage_range ult_ho_smps_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1550000, 1550000, 2325000, 2325000, 25000),
+};
+
+static struct spmi_voltage_range ult_nldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0,  375000,  375000, 1537500, 1537500, 12500),
+};
+
+static struct spmi_voltage_range ult_pldo_ranges[] = {
+       SPMI_VOLTAGE_RANGE(0, 1750000, 1750000, 3337500, 3337500, 12500),
+};
+
+static DEFINE_SPMI_SET_POINTS(pldo);
+static DEFINE_SPMI_SET_POINTS(nldo1);
+static DEFINE_SPMI_SET_POINTS(nldo2);
+static DEFINE_SPMI_SET_POINTS(nldo3);
+static DEFINE_SPMI_SET_POINTS(ln_ldo);
+static DEFINE_SPMI_SET_POINTS(smps);
+static DEFINE_SPMI_SET_POINTS(ftsmps);
+static DEFINE_SPMI_SET_POINTS(ftsmps2p5);
+static DEFINE_SPMI_SET_POINTS(boost);
+static DEFINE_SPMI_SET_POINTS(boost_byp);
+static DEFINE_SPMI_SET_POINTS(ult_lo_smps);
+static DEFINE_SPMI_SET_POINTS(ult_ho_smps);
+static DEFINE_SPMI_SET_POINTS(ult_nldo);
+static DEFINE_SPMI_SET_POINTS(ult_pldo);
+
+static inline int spmi_vreg_read(struct spmi_regulator *vreg, u16 addr, u8 *buf,
+                                int len)
+{
+       return regmap_bulk_read(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static inline int spmi_vreg_write(struct spmi_regulator *vreg, u16 addr,
+                               u8 *buf, int len)
+{
+       return regmap_bulk_write(vreg->regmap, vreg->base + addr, buf, len);
+}
+
+static int spmi_vreg_update_bits(struct spmi_regulator *vreg, u16 addr, u8 val,
+               u8 mask)
+{
+       return regmap_update_bits(vreg->regmap, vreg->base + addr, mask, val);
+}
+
+static int spmi_regulator_common_is_enabled(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_ENABLE, &reg, 1);
+
+       return (reg & SPMI_COMMON_ENABLE_MASK) == SPMI_COMMON_ENABLE;
+}
+
+static int spmi_regulator_common_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_vs_enable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       if (vreg->ocp_irq) {
+               vreg->ocp_count = 0;
+               vreg->vs_enable_time = ktime_get();
+       }
+
+       return spmi_regulator_common_enable(rdev);
+}
+
+static int spmi_regulator_common_disable(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+}
+
+static int spmi_regulator_select_voltage(struct spmi_regulator *vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int lim_min_uV, lim_max_uV, i, range_id, range_max_uV;
+
+       /* Check if request voltage is outside of physically settable range. */
+       lim_min_uV = vreg->set_points->range[0].set_point_min_uV;
+       lim_max_uV =
+         vreg->set_points->range[vreg->set_points->count - 1].set_point_max_uV;
+
+       if (uV < lim_min_uV && max_uV >= lim_min_uV)
+               uV = lim_min_uV;
+
+       if (uV < lim_min_uV || uV > lim_max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] is outside possible v=[%d, %d]\n",
+                        min_uV, max_uV, lim_min_uV, lim_max_uV);
+               return -EINVAL;
+       }
+
+       /* Find the range which uV is inside of. */
+       for (i = vreg->set_points->count - 1; i > 0; i--) {
+               range_max_uV = vreg->set_points->range[i - 1].set_point_max_uV;
+               if (uV > range_max_uV && range_max_uV > 0)
+                       break;
+       }
+
+       range_id = i;
+       range = &vreg->set_points->range[range_id];
+       *range_sel = range->range_sel;
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = (uV - range->min_uV + range->step_uV - 1)
+                       / range->step_uV;
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               dev_err(vreg->dev,
+                       "request v=[%d, %d] cannot be met by any set point; "
+                       "next set point: %d\n",
+                       min_uV, max_uV, uV);
+               return -EINVAL;
+       }
+
+       *selector = 0;
+       for (i = 0; i < range_id; i++)
+               *selector += vreg->set_points->range[i].n_voltages;
+       *selector += (uV - range->set_point_min_uV) / range->step_uV;
+
+       return 0;
+}
+
+static const struct spmi_voltage_range *
+spmi_regulator_find_range(struct spmi_regulator *vreg)
+{
+       u8 range_sel;
+       const struct spmi_voltage_range *range, *end;
+
+       range = vreg->set_points->range;
+       end = range + vreg->set_points->count;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, &range_sel, 1);
+
+       for (; range < end; range++)
+               if (range->range_sel == range_sel)
+                       return range;
+
+       return NULL;
+}
+
+static int spmi_regulator_select_voltage_same_range(struct spmi_regulator *vreg,
+               int min_uV, int max_uV, u8 *range_sel, u8 *voltage_sel,
+               unsigned *selector)
+{
+       const struct spmi_voltage_range *range;
+       int uV = min_uV;
+       int i;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               goto different_range;
+
+       if (uV < range->min_uV && max_uV >= range->min_uV)
+               uV = range->min_uV;
+
+       if (uV < range->min_uV || uV > range->max_uV) {
+               /* Current range doesn't support the requested voltage. */
+               goto different_range;
+       }
+
+       /*
+        * Force uV to be an allowed set point by applying a ceiling function to
+        * the uV value.
+        */
+       *voltage_sel = DIV_ROUND_UP(uV - range->min_uV, range->step_uV);
+       uV = *voltage_sel * range->step_uV + range->min_uV;
+
+       if (uV > max_uV) {
+               /*
+                * No set point in the current voltage range is within the
+                * requested min_uV to max_uV range.
+                */
+               goto different_range;
+       }
+
+       *selector = 0;
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (uV >= vreg->set_points->range[i].set_point_min_uV
+                   && uV <= vreg->set_points->range[i].set_point_max_uV) {
+                       *selector +=
+                           (uV - vreg->set_points->range[i].set_point_min_uV)
+                               / vreg->set_points->range[i].step_uV;
+                       break;
+               }
+
+               *selector += vreg->set_points->range[i].n_voltages;
+       }
+
+       if (*selector >= vreg->set_points->n_voltages)
+               goto different_range;
+
+       return 0;
+
+different_range:
+       return spmi_regulator_select_voltage(vreg, min_uV, max_uV,
+                       range_sel, voltage_sel, selector);
+}
+
+static int spmi_regulator_common_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 buf[2];
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible.  This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       buf[0] = range_sel;
+       buf[1] = voltage_sel;
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_RANGE, buf, 2);
+}
+
+static int spmi_regulator_set_voltage_time_sel(struct regulator_dev *rdev,
+               unsigned int old_selector, unsigned int new_selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       int diff_uV;
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       diff_uV = abs(new_selector - old_selector) * range->step_uV;
+
+       return DIV_ROUND_UP(diff_uV, vreg->slew_rate);
+}
+
+static int spmi_regulator_common_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_single_range_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, sel;
+
+       ret = spmi_regulator_select_voltage(vreg, min_uV, max_uV, &range_sel,
+               &sel, selector);
+       if (ret) {
+               dev_err(vreg->dev, "could not set voltage, ret=%d\n", ret);
+               return ret;
+       }
+
+       /*
+        * Certain types of regulators do not have a range select register so
+        * only voltage set register needs to be written.
+        */
+       return spmi_vreg_write(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &sel, 1);
+}
+
+static int spmi_regulator_single_range_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range = vreg->set_points->range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_ult_lo_smps_set_voltage(struct regulator_dev *rdev,
+               int min_uV, int max_uV, unsigned *selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int ret;
+       u8 range_sel, voltage_sel;
+
+       /*
+        * Favor staying in the current voltage range if possible. This avoids
+        * voltage spikes that occur when changing the voltage range.
+        */
+       ret = spmi_regulator_select_voltage_same_range(vreg, min_uV, max_uV,
+               &range_sel, &voltage_sel, selector);
+       if (ret)
+               return ret;
+
+       /*
+        * Calculate VSET based on range
+        * In case of range 0: voltage_sel is a 7 bit value, can be written
+        *                      witout any modification.
+        * In case of range 1: voltage_sel is a 5 bit value, bits[7-5] set to
+        *                      [011].
+        */
+       if (range_sel == 1)
+               voltage_sel |= ULT_SMPS_RANGE_SPLIT;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_VOLTAGE_SET,
+              voltage_sel, 0xff);
+}
+
+static int spmi_regulator_ult_lo_smps_get_voltage(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       const struct spmi_voltage_range *range;
+       u8 voltage_sel;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_VOLTAGE_SET, &voltage_sel, 1);
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return VOLTAGE_UNKNOWN;
+
+       if (range->range_sel == 1)
+               voltage_sel &= ~ULT_SMPS_RANGE_SPLIT;
+
+       return range->step_uV * voltage_sel + range->min_uV;
+}
+
+static int spmi_regulator_common_list_voltage(struct regulator_dev *rdev,
+                       unsigned selector)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       int uV = 0;
+       int i;
+
+       if (selector >= vreg->set_points->n_voltages)
+               return 0;
+
+       for (i = 0; i < vreg->set_points->count; i++) {
+               if (selector < vreg->set_points->range[i].n_voltages) {
+                       uV = selector * vreg->set_points->range[i].step_uV
+                               + vreg->set_points->range[i].set_point_min_uV;
+                       break;
+               }
+
+               selector -= vreg->set_points->range[i].n_voltages;
+       }
+
+       return uV;
+}
+
+static int
+spmi_regulator_common_set_bypass(struct regulator_dev *rdev, bool enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_BYPASS_MASK;
+       u8 val = 0;
+
+       if (enable)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_get_bypass(struct regulator_dev *rdev, bool *enable)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 val;
+       int ret;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &val, 1);
+       *enable = val & SPMI_COMMON_MODE_BYPASS_MASK;
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_common_get_mode(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 reg;
+
+       spmi_vreg_read(vreg, SPMI_COMMON_REG_MODE, &reg, 1);
+
+       if (reg & SPMI_COMMON_MODE_HPM_MASK)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int
+spmi_regulator_common_set_mode(struct regulator_dev *rdev, unsigned int mode)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       u8 mask = SPMI_COMMON_MODE_HPM_MASK;
+       u8 val = 0;
+
+       if (mode == REGULATOR_MODE_NORMAL)
+               val = mask;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_MODE, val, mask);
+}
+
+static int
+spmi_regulator_common_set_load(struct regulator_dev *rdev, int load_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mode;
+
+       if (load_uA >= vreg->hpm_min_load)
+               mode = REGULATOR_MODE_NORMAL;
+       else
+               mode = REGULATOR_MODE_IDLE;
+
+       return spmi_regulator_common_set_mode(rdev, mode);
+}
+
+static int spmi_regulator_common_set_pull_down(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_COMMON_PULL_DOWN_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_PULL_DOWN,
+                                    mask, mask);
+}
+
+static int spmi_regulator_common_set_soft_start(struct regulator_dev *rdev)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       unsigned int mask = SPMI_LDO_SOFT_START_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_SOFT_START,
+                                    mask, mask);
+}
+
+static int spmi_regulator_set_ilim(struct regulator_dev *rdev, int ilim_uA)
+{
+       struct spmi_regulator *vreg = rdev_get_drvdata(rdev);
+       enum spmi_regulator_logical_type type = vreg->logical_type;
+       unsigned int current_reg;
+       u8 reg;
+       u8 mask = SPMI_BOOST_CURRENT_LIMIT_MASK |
+                 SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+       int max = (SPMI_BOOST_CURRENT_LIMIT_MASK + 1) * 500;
+
+       if (type == SPMI_REGULATOR_LOGICAL_TYPE_BOOST)
+               current_reg = SPMI_BOOST_REG_CURRENT_LIMIT;
+       else
+               current_reg = SPMI_BOOST_BYP_REG_CURRENT_LIMIT;
+
+       if (ilim_uA > max || ilim_uA <= 0)
+               return -EINVAL;
+
+       reg = (ilim_uA - 1) / 500;
+       reg |= SPMI_BOOST_CURRENT_LIMIT_ENABLE_MASK;
+
+       return spmi_vreg_update_bits(vreg, current_reg, reg, mask);
+}
+
+static int spmi_regulator_vs_clear_ocp(struct spmi_regulator *vreg)
+{
+       int ret;
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_DISABLE, SPMI_COMMON_ENABLE_MASK);
+
+       vreg->vs_enable_time = ktime_get();
+
+       ret = spmi_vreg_update_bits(vreg, SPMI_COMMON_REG_ENABLE,
+               SPMI_COMMON_ENABLE, SPMI_COMMON_ENABLE_MASK);
+
+       return ret;
+}
+
+static void spmi_regulator_vs_ocp_work(struct work_struct *work)
+{
+       struct delayed_work *dwork = to_delayed_work(work);
+       struct spmi_regulator *vreg
+               = container_of(dwork, struct spmi_regulator, ocp_work);
+
+       spmi_regulator_vs_clear_ocp(vreg);
+}
+
+static irqreturn_t spmi_regulator_vs_ocp_isr(int irq, void *data)
+{
+       struct spmi_regulator *vreg = data;
+       ktime_t ocp_irq_time;
+       s64 ocp_trigger_delay_us;
+
+       ocp_irq_time = ktime_get();
+       ocp_trigger_delay_us = ktime_us_delta(ocp_irq_time,
+                                               vreg->vs_enable_time);
+
+       /*
+        * Reset the OCP count if there is a large delay between switch enable
+        * and when OCP triggers.  This is indicative of a hotplug event as
+        * opposed to a fault.
+        */
+       if (ocp_trigger_delay_us > SPMI_VS_OCP_FAULT_DELAY_US)
+               vreg->ocp_count = 0;
+
+       /* Wait for switch output to settle back to 0 V after OCP triggered. */
+       udelay(SPMI_VS_OCP_FALL_DELAY_US);
+
+       vreg->ocp_count++;
+
+       if (vreg->ocp_count == 1) {
+               /* Immediately clear the over current condition. */
+               spmi_regulator_vs_clear_ocp(vreg);
+       } else if (vreg->ocp_count <= vreg->ocp_max_retries) {
+               /* Schedule the over current clear task to run later. */
+               schedule_delayed_work(&vreg->ocp_work,
+                       msecs_to_jiffies(vreg->ocp_retry_delay_ms) + 1);
+       } else {
+               dev_err(vreg->dev,
+                       "OCP triggered %d times; no further retries\n",
+                       vreg->ocp_count);
+       }
+
+       return IRQ_HANDLED;
+}
+
+static struct regulator_ops spmi_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_ln_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+};
+
+static struct regulator_ops spmi_vs_ops = {
+       .enable                 = spmi_regulator_vs_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+static struct regulator_ops spmi_boost_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_input_current_limit = spmi_regulator_set_ilim,
+};
+
+static struct regulator_ops spmi_ftsmps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_common_set_voltage,
+       .set_voltage_time_sel   = spmi_regulator_set_voltage_time_sel,
+       .get_voltage            = spmi_regulator_common_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_lo_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_ult_lo_smps_set_voltage,
+       .get_voltage            = spmi_regulator_ult_lo_smps_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ho_smps_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+};
+
+static struct regulator_ops spmi_ult_ldo_ops = {
+       .enable                 = spmi_regulator_common_enable,
+       .disable                = spmi_regulator_common_disable,
+       .is_enabled             = spmi_regulator_common_is_enabled,
+       .set_voltage            = spmi_regulator_single_range_set_voltage,
+       .get_voltage            = spmi_regulator_single_range_get_voltage,
+       .list_voltage           = spmi_regulator_common_list_voltage,
+       .set_mode               = spmi_regulator_common_set_mode,
+       .get_mode               = spmi_regulator_common_get_mode,
+       .set_load               = spmi_regulator_common_set_load,
+       .set_bypass             = spmi_regulator_common_set_bypass,
+       .get_bypass             = spmi_regulator_common_get_bypass,
+       .set_pull_down          = spmi_regulator_common_set_pull_down,
+       .set_soft_start         = spmi_regulator_common_set_soft_start,
+};
+
+/* Maximum possible digital major revision value */
+#define INF 0xFF
+
+static const struct spmi_regulator_mapping supported_regulators[] = {
+       /*           type subtype dig_min dig_max ltype ops setpoints hpm_min */
+       SPMI_VREG(BUCK,  GP_CTL,   0, INF, SMPS,   smps,   smps,   100000),
+       SPMI_VREG(LDO,   N300,     0, INF, LDO,    ldo,    nldo1,   10000),
+       SPMI_VREG(LDO,   N600,     0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200,    0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600,     1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200,    1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N600_ST,  0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 0,   0, LDO,    ldo,    nldo2,   10000),
+       SPMI_VREG(LDO,   N600_ST,  1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   N1200_ST, 1, INF, LDO,    ldo,    nldo3,   10000),
+       SPMI_VREG(LDO,   P50,      0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   P150,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P300,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P600,     0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   P1200,    0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LN,       0, INF, LN_LDO, ln_ldo, ln_ldo,      0),
+       SPMI_VREG(LDO,   LV_P50,   0, INF, LDO,    ldo,    pldo,     5000),
+       SPMI_VREG(LDO,   LV_P150,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P300,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P600,  0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG(LDO,   LV_P1200, 0, INF, LDO,    ldo,    pldo,    10000),
+       SPMI_VREG_VS(LV100,        0, INF),
+       SPMI_VREG_VS(LV300,        0, INF),
+       SPMI_VREG_VS(MV300,        0, INF),
+       SPMI_VREG_VS(MV500,        0, INF),
+       SPMI_VREG_VS(HDMI,         0, INF),
+       SPMI_VREG_VS(OTG,          0, INF),
+       SPMI_VREG(BOOST, 5V_BOOST, 0, INF, BOOST,  boost,  boost,       0),
+       SPMI_VREG(FTS,   FTS_CTL,  0, INF, FTSMPS, ftsmps, ftsmps, 100000),
+       SPMI_VREG(FTS, FTS2p5_CTL, 0, INF, FTSMPS, ftsmps, ftsmps2p5, 100000),
+       SPMI_VREG(BOOST_BYP, BB_2A, 0, INF, BOOST_BYP, boost, boost_byp, 0),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL1, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL2, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL3, 0, INF, ULT_LO_SMPS, ult_lo_smps,
+                                               ult_lo_smps,   100000),
+       SPMI_VREG(ULT_BUCK, ULT_HF_CTL4, 0, INF, ULT_HO_SMPS, ult_ho_smps,
+                                               ult_ho_smps,   100000),
+       SPMI_VREG(ULT_LDO, N300_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N600_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N900_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, N1200_ST, 0, INF, ULT_LDO, ult_ldo, ult_nldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P150,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P300,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, LV_P450,  0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P600,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P150,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 10000),
+       SPMI_VREG(ULT_LDO, P50,     0, INF, ULT_LDO, ult_ldo, ult_pldo, 5000),
+};
+
+static void spmi_calculate_num_voltages(struct spmi_voltage_set_points *points)
+{
+       unsigned int n;
+       struct spmi_voltage_range *range = points->range;
+
+       for (; range < points->range + points->count; range++) {
+               n = 0;
+               if (range->set_point_max_uV) {
+                       n = range->set_point_max_uV - range->set_point_min_uV;
+                       n = (n / range->step_uV) + 1;
+               }
+               range->n_voltages = n;
+               points->n_voltages += n;
+       }
+}
+
+static int spmi_regulator_match(struct spmi_regulator *vreg, u16 force_type)
+{
+       const struct spmi_regulator_mapping *mapping;
+       int ret, i;
+       u32 dig_major_rev;
+       u8 version[SPMI_COMMON_REG_SUBTYPE - SPMI_COMMON_REG_DIG_MAJOR_REV + 1];
+       u8 type, subtype;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_DIG_MAJOR_REV, version,
+               ARRAY_SIZE(version));
+       if (ret) {
+               dev_err(vreg->dev, "could not read version registers\n");
+               return ret;
+       }
+       dig_major_rev   = version[SPMI_COMMON_REG_DIG_MAJOR_REV
+                                       - SPMI_COMMON_REG_DIG_MAJOR_REV];
+       if (!force_type) {
+               type            = version[SPMI_COMMON_REG_TYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+               subtype         = version[SPMI_COMMON_REG_SUBTYPE -
+                                         SPMI_COMMON_REG_DIG_MAJOR_REV];
+       } else {
+               type = force_type >> 8;
+               subtype = force_type;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(supported_regulators); i++) {
+               mapping = &supported_regulators[i];
+               if (mapping->type == type && mapping->subtype == subtype
+                   && mapping->revision_min <= dig_major_rev
+                   && mapping->revision_max >= dig_major_rev)
+                       goto found;
+       }
+
+       dev_err(vreg->dev,
+               "unsupported regulator: name=%s type=0x%02X, subtype=0x%02X, dig major rev=0x%02X\n",
+               vreg->desc.name, type, subtype, dig_major_rev);
+
+       return -ENODEV;
+
+found:
+       vreg->logical_type      = mapping->logical_type;
+       vreg->set_points        = mapping->set_points;
+       vreg->hpm_min_load      = mapping->hpm_min_load;
+       vreg->desc.ops          = mapping->ops;
+
+       if (mapping->set_points) {
+               if (!mapping->set_points->n_voltages)
+                       spmi_calculate_num_voltages(mapping->set_points);
+               vreg->desc.n_voltages = mapping->set_points->n_voltages;
+       }
+
+       return 0;
+}
+
+static int spmi_regulator_ftsmps_init_slew_rate(struct spmi_regulator *vreg)
+{
+       int ret;
+       u8 reg = 0;
+       int step, delay, slew_rate;
+       const struct spmi_voltage_range *range;
+
+       ret = spmi_vreg_read(vreg, SPMI_COMMON_REG_STEP_CTRL, &reg, 1);
+       if (ret) {
+               dev_err(vreg->dev, "spmi read failed, ret=%d\n", ret);
+               return ret;
+       }
+
+       range = spmi_regulator_find_range(vreg);
+       if (!range)
+               return -EINVAL;
+
+       step = reg & SPMI_FTSMPS_STEP_CTRL_STEP_MASK;
+       step >>= SPMI_FTSMPS_STEP_CTRL_STEP_SHIFT;
+
+       delay = reg & SPMI_FTSMPS_STEP_CTRL_DELAY_MASK;
+       delay >>= SPMI_FTSMPS_STEP_CTRL_DELAY_SHIFT;
+
+       /* slew_rate has units of uV/us */
+       slew_rate = SPMI_FTSMPS_CLOCK_RATE * range->step_uV * (1 << step);
+       slew_rate /= 1000 * (SPMI_FTSMPS_STEP_DELAY << delay);
+       slew_rate *= SPMI_FTSMPS_STEP_MARGIN_NUM;
+       slew_rate /= SPMI_FTSMPS_STEP_MARGIN_DEN;
+
+       /* Ensure that the slew rate is greater than 0 */
+       vreg->slew_rate = max(slew_rate, 1);
+
+       return ret;
+}
+
+static unsigned int spmi_regulator_of_map_mode(unsigned int mode)
+{
+       if (mode)
+               return REGULATOR_MODE_NORMAL;
+
+       return REGULATOR_MODE_IDLE;
+}
+
+static int spmi_regulator_of_parse(struct device_node *node,
+                           const struct regulator_desc *desc,
+                           struct regulator_config *config)
+{
+       struct spmi_regulator *vreg = config->driver_data;
+       struct device *dev = config->dev;
+       int ret;
+
+       vreg->ocp_max_retries = SPMI_VS_OCP_DEFAULT_MAX_RETRIES;
+       vreg->ocp_retry_delay_ms = SPMI_VS_OCP_DEFAULT_RETRY_DELAY_MS;
+
+       if (vreg->logical_type == SPMI_REGULATOR_LOGICAL_TYPE_FTSMPS) {
+               ret = spmi_regulator_ftsmps_init_slew_rate(vreg);
+               if (ret)
+                       return ret;
+       }
+
+       if (vreg->logical_type != SPMI_REGULATOR_LOGICAL_TYPE_VS)
+               vreg->ocp_irq = 0;
+
+       if (vreg->ocp_irq) {
+               ret = devm_request_irq(dev, vreg->ocp_irq,
+                       spmi_regulator_vs_ocp_isr, IRQF_TRIGGER_RISING, "ocp",
+                       vreg);
+               if (ret < 0) {
+                       dev_err(dev, "failed to request irq %d, ret=%d\n",
+                               vreg->ocp_irq, ret);
+                       return ret;
+               }
+
+               INIT_DELAYED_WORK(&vreg->ocp_work, spmi_regulator_vs_ocp_work);
+       }
+
+       return 0;
+}
+
+static const struct spmi_regulator_data pm8941_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2_lvs_1_2_3", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l11", },
+       { "l5", 0x4400, "vdd_l5_l7", NULL, 0x0410 },
+       { "l6", 0x4500, "vdd_l6_l12_l14_l15", },
+       { "l7", 0x4600, "vdd_l5_l7", NULL, 0x0410 },
+       { "l8", 0x4700, "vdd_l8_l16_l18_19", },
+       { "l9", 0x4800, "vdd_l9_l10_l17_l22", },
+       { "l10", 0x4900, "vdd_l9_l10_l17_l22", },
+       { "l11", 0x4a00, "vdd_l4_l11", },
+       { "l12", 0x4b00, "vdd_l6_l12_l14_l15", },
+       { "l13", 0x4c00, "vdd_l13_l20_l23_l24", },
+       { "l14", 0x4d00, "vdd_l6_l12_l14_l15", },
+       { "l15", 0x4e00, "vdd_l6_l12_l14_l15", },
+       { "l16", 0x4f00, "vdd_l8_l16_l18_19", },
+       { "l17", 0x5000, "vdd_l9_l10_l17_l22", },
+       { "l18", 0x5100, "vdd_l8_l16_l18_19", },
+       { "l19", 0x5200, "vdd_l8_l16_l18_19", },
+       { "l20", 0x5300, "vdd_l13_l20_l23_l24", },
+       { "l21", 0x5400, "vdd_l21", },
+       { "l22", 0x5500, "vdd_l9_l10_l17_l22", },
+       { "l23", 0x5600, "vdd_l13_l20_l23_l24", },
+       { "l24", 0x5700, "vdd_l13_l20_l23_l24", },
+       { "lvs1", 0x8000, "vdd_l2_lvs_1_2_3", },
+       { "lvs2", 0x8100, "vdd_l2_lvs_1_2_3", },
+       { "lvs3", 0x8200, "vdd_l2_lvs_1_2_3", },
+       { "mvs1", 0x8300, "vin_5vs", },
+       { "mvs2", 0x8400, "vin_5vs", },
+       { }
+};
+
+static const struct spmi_regulator_data pm8841_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", NULL, 0x1c08 },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", NULL, 0x1c08 },
+       { "s5", 0x2000, "vdd_s5", NULL, 0x1c08 },
+       { "s6", 0x2300, "vdd_s6", NULL, 0x1c08 },
+       { "s7", 0x2600, "vdd_s7", NULL, 0x1c08 },
+       { "s8", 0x2900, "vdd_s8", NULL, 0x1c08 },
+       { }
+};
+
+static const struct spmi_regulator_data pm8916_regulators[] = {
+       { "s1", 0x1400, "vdd_s1", },
+       { "s2", 0x1700, "vdd_s2", },
+       { "s3", 0x1a00, "vdd_s3", },
+       { "s4", 0x1d00, "vdd_s4", },
+       { "l1", 0x4000, "vdd_l1_l3", },
+       { "l2", 0x4100, "vdd_l2", },
+       { "l3", 0x4200, "vdd_l1_l3", },
+       { "l4", 0x4300, "vdd_l4_l5_l6", },
+       { "l5", 0x4400, "vdd_l4_l5_l6", },
+       { "l6", 0x4500, "vdd_l4_l5_l6", },
+       { "l7", 0x4600, "vdd_l7", },
+       { "l8", 0x4700, "vdd_l8_l11_l14_l15_l16", },
+       { "l9", 0x4800, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l10", 0x4900, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l11", 0x4a00, "vdd_l8_l11_l14_l15_l16", },
+       { "l12", 0x4b00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l13", 0x4c00, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l14", 0x4d00, "vdd_l8_l11_l14_l15_l16", },
+       { "l15", 0x4e00, "vdd_l8_l11_l14_l15_l16", },
+       { "l16", 0x4f00, "vdd_l8_l11_l14_l15_l16", },
+       { "l17", 0x5000, "vdd_l9_l10_l12_l13_l17_l18", },
+       { "l18", 0x5100, "vdd_l9_l10_l12_l13_l17_l18", },
+       { }
+};
+
+static const struct of_device_id qcom_spmi_regulator_match[] = {
+       { .compatible = "qcom,pm8841-regulators", .data = &pm8841_regulators },
+       { .compatible = "qcom,pm8916-regulators", .data = &pm8916_regulators },
+       { .compatible = "qcom,pm8941-regulators", .data = &pm8941_regulators },
+       { }
+};
+MODULE_DEVICE_TABLE(of, qcom_spmi_regulator_match);
+
+static int qcom_spmi_regulator_probe(struct platform_device *pdev)
+{
+       const struct spmi_regulator_data *reg;
+       const struct of_device_id *match;
+       struct regulator_config config = { };
+       struct regulator_dev *rdev;
+       struct spmi_regulator *vreg;
+       struct regmap *regmap;
+       const char *name;
+       struct device *dev = &pdev->dev;
+       int ret;
+       struct list_head *vreg_list;
+
+       vreg_list = devm_kzalloc(dev, sizeof(*vreg_list), GFP_KERNEL);
+       if (!vreg_list)
+               return -ENOMEM;
+       INIT_LIST_HEAD(vreg_list);
+       platform_set_drvdata(pdev, vreg_list);
+
+       regmap = dev_get_regmap(dev->parent, NULL);
+       if (!regmap)
+               return -ENODEV;
+
+       match = of_match_device(qcom_spmi_regulator_match, &pdev->dev);
+       if (!match)
+               return -ENODEV;
+
+       for (reg = match->data; reg->name; reg++) {
+               vreg = devm_kzalloc(dev, sizeof(*vreg), GFP_KERNEL);
+               if (!vreg)
+                       return -ENOMEM;
+
+               vreg->dev = dev;
+               vreg->base = reg->base;
+               vreg->regmap = regmap;
+
+               if (reg->ocp) {
+                       vreg->ocp_irq = platform_get_irq_byname(pdev, reg->ocp);
+                       if (vreg->ocp_irq < 0) {
+                               ret = vreg->ocp_irq;
+                               goto err;
+                       }
+               }
+
+               vreg->desc.id = -1;
+               vreg->desc.owner = THIS_MODULE;
+               vreg->desc.type = REGULATOR_VOLTAGE;
+               vreg->desc.name = name = reg->name;
+               vreg->desc.supply_name = reg->supply;
+               vreg->desc.of_match = reg->name;
+               vreg->desc.of_parse_cb = spmi_regulator_of_parse;
+               vreg->desc.of_map_mode = spmi_regulator_of_map_mode;
+
+               ret = spmi_regulator_match(vreg, reg->force_type);
+               if (ret)
+                       goto err;
+
+               config.dev = dev;
+               config.driver_data = vreg;
+               rdev = devm_regulator_register(dev, &vreg->desc, &config);
+               if (IS_ERR(rdev)) {
+                       dev_err(dev, "failed to register %s\n", name);
+                       ret = PTR_ERR(rdev);
+                       goto err;
+               }
+
+               INIT_LIST_HEAD(&vreg->node);
+               list_add(&vreg->node, vreg_list);
+       }
+
+       return 0;
+
+err:
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+       return ret;
+}
+
+static int qcom_spmi_regulator_remove(struct platform_device *pdev)
+{
+       struct spmi_regulator *vreg;
+       struct list_head *vreg_list = platform_get_drvdata(pdev);
+
+       list_for_each_entry(vreg, vreg_list, node)
+               if (vreg->ocp_irq)
+                       cancel_delayed_work_sync(&vreg->ocp_work);
+
+       return 0;
+}
+
+static struct platform_driver qcom_spmi_regulator_driver = {
+       .driver         = {
+               .name   = "qcom-spmi-regulator",
+               .of_match_table = qcom_spmi_regulator_match,
+       },
+       .probe          = qcom_spmi_regulator_probe,
+       .remove         = qcom_spmi_regulator_remove,
+};
+module_platform_driver(qcom_spmi_regulator_driver);
+
+MODULE_DESCRIPTION("Qualcomm SPMI PMIC regulator driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:qcom-spmi-regulator");
index ff828117798fd3f4775cd5cc6c8e86d8fbe33a00..326ffb55337117d6dc2a96d7fce9cea773e18a14 100644 (file)
@@ -515,7 +515,7 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev)
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg,
                        rdev->desc->enable_mask, val);
@@ -538,7 +538,7 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
                default:
                        state = S2MPS14_ENABLE_SUSPEND;
                        break;
-               };
+               }
                break;
        case S2MPU02:
                switch (rdev_id) {
@@ -552,11 +552,11 @@ static int s2mps14_regulator_set_suspend_disable(struct regulator_dev *rdev)
                default:
                        state = S2MPU02_ENABLE_SUSPEND;
                        break;
-               };
+               }
                break;
        default:
                return -EINVAL;
-       };
+       }
 
        ret = regmap_read(rdev->regmap, rdev->desc->enable_reg, &val);
        if (ret < 0)
@@ -977,7 +977,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev)
                dev_err(&pdev->dev, "Invalid device type: %u\n",
                                    s2mps11->dev_type);
                return -EINVAL;
-       };
+       }
 
        s2mps11->ext_control_gpio = devm_kmalloc(&pdev->dev,
                        sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num,
index 0d7e164a5e763939a2d15d3e0d6a160beabd23c7..8cbb82ceec40521a7486b4f89b03e1c42499c041 100644 (file)
@@ -533,7 +533,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
@@ -543,7 +544,8 @@ static int wm831x_buckv_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "HC"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_oc_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request HC IRQ %d: %d\n",
                        irq, ret);
@@ -669,7 +671,8 @@ static int wm831x_buckp_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name, dcdc);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name, dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
@@ -785,7 +788,8 @@ static int wm831x_boostp_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_dcdc_uv_irq,
-                                       IRQF_TRIGGER_RISING, dcdc->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       dcdc->name,
                                        dcdc);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
index 1e88391a162849c0c529e2f76e28f95216e576f8..1442828fcd9afbe44e2788646f4ed992b903d7ea 100644 (file)
@@ -204,7 +204,8 @@ static int wm831x_isink_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_isink_irq,
-                                       IRQF_TRIGGER_RISING, isink->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       isink->name,
                                        isink);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request ISINK IRQ %d: %d\n",
index 7ae2dc82f636b601c2a976674dc26c1a9aae02a6..5a7b65e8a529c496edfdcf15ade002e82e4d5d6d 100644 (file)
@@ -287,7 +287,8 @@ static int wm831x_gp_ldo_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_ldo_uv_irq,
-                                       IRQF_TRIGGER_RISING, ldo->name,
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       ldo->name,
                                        ldo);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
@@ -496,7 +497,8 @@ static int wm831x_aldo_probe(struct platform_device *pdev)
        irq = wm831x_irq(wm831x, platform_get_irq_byname(pdev, "UV"));
        ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
                                        wm831x_ldo_uv_irq,
-                                       IRQF_TRIGGER_RISING, ldo->name, ldo);
+                                       IRQF_TRIGGER_RISING | IRQF_ONESHOT,
+                                       ldo->name, ldo);
        if (ret != 0) {
                dev_err(&pdev->dev, "Failed to request UV IRQ %d: %d\n",
                        irq, ret);
index fffa688ac3a7a36c9ecc33e006c52f1c4cf3892c..4db9fbe4889d3c54f76c2c2c1e3d50ce404d0965 100644 (file)
@@ -91,6 +91,7 @@ struct regulator_linear_range {
  * @set_current_limit: Configure a limit for a current-limited regulator.
  *                     The driver should select the current closest to max_uA.
  * @get_current_limit: Get the configured limit for a current-limited regulator.
+ * @set_input_current_limit: Configure an input limit.
  *
  * @set_mode: Set the configured operating mode for the regulator.
  * @get_mode: Get the configured operating mode for the regulator.
@@ -111,6 +112,7 @@ struct regulator_linear_range {
  *               to stabilise after being set to a new value, in microseconds.
  *               The function provides the from and to voltage selector, the
  *               function should return the worst case.
+ * @set_soft_start: Enable soft start for the regulator.
  *
  * @set_suspend_voltage: Set the voltage for the regulator when the system
  *                       is suspended.
@@ -121,6 +123,9 @@ struct regulator_linear_range {
  * @set_suspend_mode: Set the operating mode for the regulator when the
  *                    system is suspended.
  *
+ * @set_pull_down: Configure the regulator to pull down when the regulator
+ *                is disabled.
+ *
  * This struct describes regulator operations which can be implemented by
  * regulator chip drivers.
  */
@@ -142,6 +147,8 @@ struct regulator_ops {
                                 int min_uA, int max_uA);
        int (*get_current_limit) (struct regulator_dev *);
 
+       int (*set_input_current_limit) (struct regulator_dev *, int lim_uA);
+
        /* enable/disable regulator */
        int (*enable) (struct regulator_dev *);
        int (*disable) (struct regulator_dev *);
@@ -158,6 +165,8 @@ struct regulator_ops {
                                     unsigned int old_selector,
                                     unsigned int new_selector);
 
+       int (*set_soft_start) (struct regulator_dev *);
+
        /* report regulator status ... most other accessors report
         * control inputs, this reports results of combining inputs
         * from Linux (and other sources) with the actual load.
@@ -187,6 +196,8 @@ struct regulator_ops {
 
        /* set regulator suspend operating mode (defined in consumer.h) */
        int (*set_suspend_mode) (struct regulator_dev *, unsigned int mode);
+
+       int (*set_pull_down) (struct regulator_dev *);
 };
 
 /*
index b07562e082c416a2b98fc08818f35111c8237288..b11be126012974ebf4877220c8949310d649ddea 100644 (file)
@@ -75,6 +75,8 @@ struct regulator_state {
  *
  * @min_uA: Smallest current consumers may set.
  * @max_uA: Largest current consumers may set.
+ * @ilim_uA: Maximum input current.
+ * @system_load: Load that isn't captured by any consumer requests.
  *
  * @valid_modes_mask: Mask of modes which may be configured by consumers.
  * @valid_ops_mask: Operations which may be performed by consumers.
@@ -86,6 +88,8 @@ struct regulator_state {
  *           applied.
  * @apply_uV: Apply the voltage constraint when initialising.
  * @ramp_disable: Disable ramp delay when initialising or when setting voltage.
+ * @soft_start: Enable soft start so that voltage ramps slowly.
+ * @pull_down: Enable pull down when regulator is disabled.
  *
  * @input_uV: Input voltage for regulator when supplied by another regulator.
  *
@@ -111,6 +115,9 @@ struct regulation_constraints {
        /* current output range (inclusive) - for current control */
        int min_uA;
        int max_uA;
+       int ilim_uA;
+
+       int system_load;
 
        /* valid regulator operating modes for this machine */
        unsigned int valid_modes_mask;
@@ -138,6 +145,8 @@ struct regulation_constraints {
        unsigned boot_on:1;     /* bootloader/firmware enabled regulator */
        unsigned apply_uV:1;    /* apply uV constraint if min == max */
        unsigned ramp_disable:1; /* disable ramp delay */
+       unsigned soft_start:1;  /* ramp voltage slowly */
+       unsigned pull_down:1;   /* pull down resistor when regulator off */
 };
 
 /**
index f8acc052e35326ebc818809bc921896c81c7fd02..f6a8a16a0d4deb27dc232f782a13453a5ba73ce0 100644 (file)
@@ -58,6 +58,9 @@
  *             control signal from EN input pin. If it is false then
  *             voltage output will be enabled/disabled through EN bit of
  *             device register.
+ * @enable_gpio: Enable GPIO. If EN pin is controlled through GPIO from host
+ *             then GPIO number can be provided. If no GPIO controlled then
+ *             it should be -1.
  * @dvs_gpio: GPIO for dvs. It should be -1 if this is tied with fixed logic.
  * @dvs_def_state: Default state of dvs. 1 if it is high else 0.
  */
@@ -65,6 +68,7 @@ struct max8973_regulator_platform_data {
        struct regulator_init_data *reg_init_data;
        unsigned long control_flags;
        bool enable_ext_control;
+       int enable_gpio;
        int dvs_gpio;
        unsigned dvs_def_state:1;
 };