aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGuodong Xu <guodong.xu@linaro.org>2013-08-16 16:28:51 +0800
committerZhangfei Gao <zhangfei.gao@linaro.org>2013-08-19 17:14:20 +0800
commit5ed366b3739224be33f95d9a08753887b384f418 (patch)
treec0348817e0d02cdaab7b36a54bc0d66bf4a6f5fa
parent2903e34ac7e8860dec26ec2e9f7d03c9351e72ef (diff)
regulator: hi6421: Add support to hi6421 regulators
Add support to hi6421 regulators. Hi6421 is a multi-functional PMIC manufactured by HiSilicon Inc. Signed-off-by: Guodong Xu <guodong.xu@linaro.org> Acked-by: Haojian Zhuang <haojian.zhuang@linaro.org>
-rw-r--r--drivers/mfd/hi6421-pmic-core.c2
-rw-r--r--drivers/regulator/Kconfig7
-rw-r--r--drivers/regulator/Makefile2
-rw-r--r--drivers/regulator/hi6421-regulator.c557
-rw-r--r--include/linux/mfd/hi6421-pmic.h15
5 files changed, 582 insertions, 1 deletions
diff --git a/drivers/mfd/hi6421-pmic-core.c b/drivers/mfd/hi6421-pmic-core.c
index f7fd7965dca1..b2d7c4e82bd2 100644
--- a/drivers/mfd/hi6421-pmic-core.c
+++ b/drivers/mfd/hi6421-pmic-core.c
@@ -27,6 +27,7 @@
#include <linux/err.h>
#include <linux/interrupt.h>
#include <linux/io.h>
+#include <linux/mutex.h>
#include <linux/platform_device.h>
#include <linux/of.h>
#include <linux/of_address.h>
@@ -203,6 +204,7 @@ static int hi6421_pmic_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ mutex_init(&pmic->enable_mutex);
/* get resources */
pmic->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!pmic->res) {
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8bb26446037e..08774c02a840 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -514,5 +514,12 @@ config REGULATOR_AS3711
This driver provides support for the voltage regulators on the
AS3711 PMIC
+config REGULATOR_HI6421
+ tristate "HiSilicon Hi6421 PMIC regulators"
+ depends on MFD_HI6421_PMIC
+ help
+ This driver provides support for the voltage regulators on the
+ HiSilicon Hi6421 PMU / Codec IC.
+
endif
diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile
index 47a34ff88f98..5a67225c8973 100644
--- a/drivers/regulator/Makefile
+++ b/drivers/regulator/Makefile
@@ -70,6 +70,6 @@ obj-$(CONFIG_REGULATOR_WM831X) += wm831x-ldo.o
obj-$(CONFIG_REGULATOR_WM8350) += wm8350-regulator.o
obj-$(CONFIG_REGULATOR_WM8400) += wm8400-regulator.o
obj-$(CONFIG_REGULATOR_WM8994) += wm8994-regulator.o
-
+obj-$(CONFIG_REGULATOR_HI6421) += hi6421-regulator.o
ccflags-$(CONFIG_REGULATOR_DEBUG) += -DDEBUG
diff --git a/drivers/regulator/hi6421-regulator.c b/drivers/regulator/hi6421-regulator.c
new file mode 100644
index 000000000000..a48c9501030a
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,557 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) 2013 Linaro Ltd.
+ * Copyright (c) 2011 Hisilicon.
+ *
+ * Guodong Xu <guodong.xu@linaro.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed 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.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/module.h>
+#include <linux/err.h>
+#include <linux/io.h>
+#include <linux/platform_device.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_address.h>
+#include <linux/regmap.h>
+#include <linux/regulator/driver.h>
+#include <linux/regulator/machine.h>
+#include <linux/regulator/of_regulator.h>
+#include <linux/mfd/hi6421-pmic.h>
+#include <linux/delay.h>
+#include <linux/time.h>
+
+struct hi6421_regulator_register_info {
+ u32 ctrl_reg;
+ u32 enable_mask;
+ u32 eco_mode_mask;
+ u32 vset_reg;
+ u32 vset_mask;
+};
+
+struct hi6421_regulator {
+ const char *name;
+ struct hi6421_regulator_register_info register_info;
+ struct timeval last_off_time;
+ u32 off_on_delay;
+ u32 eco_uA;
+ struct regulator_desc rdesc;
+ int (*dt_parse)(struct hi6421_regulator *, struct platform_device *);
+};
+
+static inline struct hi6421_pmic *rdev_to_pmic(struct regulator_dev *dev)
+{
+ /* regulator_dev parent to->
+ * hi6421 regulator platform device_dev parent to->
+ * hi6421 pmic platform device_dev
+ */
+ return dev_get_drvdata(rdev_get_dev(dev)->parent->parent);
+}
+
+/* helper function to ensure when it returns it is at least 'delay_us'
+ * microseconds after 'since'.
+ */
+static void ensured_time_after(struct timeval since, u32 delay_us)
+{
+ struct timeval now;
+ u64 elapsed_ns64, delay_ns64;
+ u32 actual_us32;
+
+ delay_ns64 = delay_us * NSEC_PER_USEC;
+ do_gettimeofday(&now);
+ elapsed_ns64 = timeval_to_ns(&now) - timeval_to_ns(&since);
+ if (delay_ns64 > elapsed_ns64) {
+ actual_us32 = ((u32)(delay_ns64 - elapsed_ns64) /
+ NSEC_PER_USEC);
+ if (actual_us32 >= 1000) {
+ mdelay(actual_us32 / 1000);
+ udelay(actual_us32 % 1000);
+ } else if (actual_us32 > 0) {
+ udelay(actual_us32);
+ }
+ }
+ return;
+}
+
+static int hi6421_regulator_is_enabled(struct regulator_dev *dev)
+{
+ u32 reg_val;
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+
+ return ((reg_val & sreg->register_info.enable_mask) != 0);
+}
+
+static int hi6421_regulator_enable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* keep a distance of off_on_delay from last time disabled */
+ ensured_time_after(sreg->last_off_time, sreg->off_on_delay);
+
+ /* cannot enable more than one regulator at one time */
+ mutex_lock(&pmic->enable_mutex);
+ ensured_time_after(pmic->last_enabled, HI6421_REGS_ENA_PROTECT_TIME);
+
+ /* set enable register */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask,
+ sreg->register_info.enable_mask);
+
+ do_gettimeofday(&pmic->last_enabled);
+ mutex_unlock(&pmic->enable_mutex);
+
+ return 0;
+}
+
+static int hi6421_regulator_disable(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+
+ /* set enable register to 0 */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.enable_mask, 0);
+
+ do_gettimeofday(&sreg->last_off_time);
+
+ return 0;
+}
+
+static int hi6421_regulator_get_voltage(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val, selector;
+
+ /* get voltage selector */
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.vset_reg);
+ selector = (reg_val & sreg->register_info.vset_mask) >>
+ (ffs(sreg->register_info.vset_mask) - 1);
+
+ return sreg->rdesc.ops->list_voltage(dev, selector);
+}
+
+static int hi6421_regulator_ldo_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ for (vsel = 0; vsel < sreg->rdesc.n_voltages; vsel++) {
+ int uV = sreg->rdesc.volt_table[vsel];
+ /* Break at the first in-range value */
+ if (min_uV <= uV && uV <= max_uV)
+ break;
+ }
+
+ /* unlikely to happen. sanity test done by regulator core */
+ if (unlikely(vsel == sreg->rdesc.n_voltages))
+ return -EINVAL;
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static int hi6421_regulator_buck012_set_voltage(struct regulator_dev *dev,
+ int min_uV, int max_uV, unsigned *selector)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 vsel;
+ int ret = 0;
+
+ vsel = DIV_ROUND_UP((max_uV - sreg->rdesc.min_uV),
+ sreg->rdesc.uV_step);
+
+ *selector = vsel;
+ /* set voltage selector */
+ hi6421_pmic_rmw(pmic, sreg->register_info.vset_reg,
+ sreg->register_info.vset_mask,
+ vsel << (ffs(sreg->register_info.vset_mask) - 1));
+
+ return ret;
+}
+
+static unsigned int hi6421_regulator_get_mode(struct regulator_dev *dev)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 reg_val;
+
+ reg_val = hi6421_pmic_read(pmic, sreg->register_info.ctrl_reg);
+ if (reg_val & sreg->register_info.eco_mode_mask)
+ return REGULATOR_MODE_IDLE;
+ else
+ return REGULATOR_MODE_NORMAL;
+}
+
+static int hi6421_regulator_set_mode(struct regulator_dev *dev,
+ unsigned int mode)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+ struct hi6421_pmic *pmic = rdev_to_pmic(dev);
+ u32 eco_mode;
+
+ switch (mode) {
+ case REGULATOR_MODE_NORMAL:
+ eco_mode = HI6421_ECO_MODE_DISABLE;
+ break;
+ case REGULATOR_MODE_IDLE:
+ eco_mode = HI6421_ECO_MODE_ENABLE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* set mode */
+ hi6421_pmic_rmw(pmic, sreg->register_info.ctrl_reg,
+ sreg->register_info.eco_mode_mask,
+ eco_mode << (ffs(sreg->register_info.eco_mode_mask) - 1));
+
+ return 0;
+}
+
+
+unsigned int hi6421_regulator_get_optimum_mode(struct regulator_dev *dev,
+ int input_uV, int output_uV, int load_uA)
+{
+ struct hi6421_regulator *sreg = rdev_get_drvdata(dev);
+
+ if ((load_uA == 0) || (load_uA > sreg->eco_uA))
+ return REGULATOR_MODE_NORMAL;
+ else
+ return REGULATOR_MODE_IDLE;
+}
+
+static int hi6421_dt_parse_common(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int register_info[3];
+ int ret = 0;
+
+ /* parse .register_info.ctrl_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-ctrl",
+ register_info, 3);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-ctrl property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.ctrl_reg = register_info[0];
+ sreg->register_info.enable_mask = register_info[1];
+ sreg->register_info.eco_mode_mask = register_info[2];
+
+ /* parse .register_info.vset_reg */
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset",
+ register_info, 2);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset property set\n");
+ goto dt_parse_common_end;
+ }
+ sreg->register_info.vset_reg = register_info[0];
+ sreg->register_info.vset_mask = register_info[1];
+
+ /* parse .off-on-delay */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-off-on-delay-us",
+ &sreg->off_on_delay);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-off-on-delay-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .enable_time */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-enable-time-us",
+ &rdesc->enable_time);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-enable-time-us property set\n");
+ goto dt_parse_common_end;
+ }
+
+ /* parse .eco_uA */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-eco-microamp",
+ &sreg->eco_uA);
+ if (ret) {
+ sreg->eco_uA = 0;
+ ret = 0;
+ }
+
+dt_parse_common_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_ldo(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ unsigned int *v_table;
+ int ret = 0;
+
+ /* parse .n_voltages, and .volt_table */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_ldo_end;
+ }
+
+ /* alloc space for .volt_table */
+ v_table = devm_kzalloc(dev, sizeof(unsigned int) * rdesc->n_voltages,
+ GFP_KERNEL);
+ if (unlikely(!v_table)) {
+ ret = -ENOMEM;
+ dev_err(dev, "no memory for .volt_table\n");
+ goto dt_parse_ldo_end;
+ }
+
+ ret = of_property_read_u32_array(np, "hisilicon,hi6421-vset-table",
+ v_table, rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-vset-table property set\n");
+ goto dt_parse_ldo_end;
+ }
+ rdesc->volt_table = v_table;
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_ldo_end;
+ }
+
+dt_parse_ldo_end:
+ return ret;
+}
+
+static int hi6421_dt_parse_buck012(struct hi6421_regulator *sreg,
+ struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc = &sreg->rdesc;
+ int ret = 0;
+
+ /* parse .n_voltages, and .uV_step */
+ ret = of_property_read_u32(np, "hisilicon,hi6421-n-voltages",
+ &rdesc->n_voltages);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-n-voltages property set\n");
+ goto dt_parse_buck012_end;
+ }
+ ret = of_property_read_u32(np, "hisilicon,hi6421-uv-step",
+ &rdesc->uV_step);
+ if (ret) {
+ dev_err(dev, "no hisilicon,hi6421-uv-step property set\n");
+ goto dt_parse_buck012_end;
+ }
+
+ /* parse hi6421 regulator's dt common part */
+ ret = hi6421_dt_parse_common(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "failure in hi6421_dt_parse_common\n");
+ goto dt_parse_buck012_end;
+ }
+
+dt_parse_buck012_end:
+ return ret;
+}
+
+static struct regulator_ops hi6421_ldo_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck012_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_linear,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_buck012_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static struct regulator_ops hi6421_buck345_rops = {
+ .is_enabled = hi6421_regulator_is_enabled,
+ .enable = hi6421_regulator_enable,
+ .disable = hi6421_regulator_disable,
+ .list_voltage = regulator_list_voltage_table,
+ .get_voltage = hi6421_regulator_get_voltage,
+ .set_voltage = hi6421_regulator_ldo_set_voltage,
+ .get_mode = hi6421_regulator_get_mode,
+ .set_mode = hi6421_regulator_set_mode,
+ .get_optimum_mode = hi6421_regulator_get_optimum_mode,
+};
+
+static const struct hi6421_regulator hi6421_regulator_ldo = {
+ .rdesc = {
+ .ops = &hi6421_ldo_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck012 = {
+ .rdesc = {
+ .ops = &hi6421_buck012_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_buck012,
+};
+
+static const struct hi6421_regulator hi6421_regulator_buck345 = {
+ .rdesc = {
+ .ops = &hi6421_buck345_rops,
+ .type = REGULATOR_VOLTAGE,
+ .owner = THIS_MODULE,
+ },
+ .dt_parse = hi6421_dt_parse_ldo,
+};
+
+static struct of_device_id of_hi6421_regulator_match_tbl[] = {
+ {
+ .compatible = "hisilicon,hi6421-ldo",
+ .data = &hi6421_regulator_ldo,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck012",
+ .data = &hi6421_regulator_buck012,
+ },
+ {
+ .compatible = "hisilicon,hi6421-buck345",
+ .data = &hi6421_regulator_buck345,
+ },
+ { /* end */ }
+};
+
+static int hi6421_regulator_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *np = dev->of_node;
+ struct regulator_desc *rdesc;
+ struct regulator_dev *rdev;
+ struct hi6421_regulator *sreg = NULL;
+ struct regulator_init_data *initdata;
+ struct regulation_constraints *c;
+ struct regulator_config config = { };
+ const struct of_device_id *match;
+ const struct hi6421_regulator *template = NULL;
+ int ret = 0;
+
+ /* to check which type of regulator this is */
+ match = of_match_device(of_hi6421_regulator_match_tbl, &pdev->dev);
+ if (match)
+ template = match->data;
+ else
+ return -EINVAL;
+
+ initdata = of_get_regulator_init_data(dev, np);
+
+ /* hi6421 regulator supports two modes */
+ c = &initdata->constraints;
+ c->valid_modes_mask = REGULATOR_MODE_NORMAL | REGULATOR_MODE_IDLE;
+ c->valid_ops_mask |= (REGULATOR_CHANGE_MODE | REGULATOR_CHANGE_DRMS);
+ c->input_uV = c->min_uV;
+
+ sreg = devm_kzalloc(dev, sizeof(*sreg), GFP_KERNEL);
+ if (sreg == NULL)
+ return -ENOMEM;
+ memcpy(sreg, template, sizeof(*sreg));
+
+ sreg->name = initdata->constraints.name;
+ rdesc = &sreg->rdesc;
+ rdesc->name = sreg->name;
+ rdesc->min_uV = initdata->constraints.min_uV;
+
+ /* to parse device tree data for regulator specific */
+ ret = sreg->dt_parse(sreg, pdev);
+ if (ret) {
+ dev_err(dev, "device tree parameter parse error!\n");
+ goto hi6421_probe_end;
+ }
+
+ config.dev = &pdev->dev;
+ config.init_data = initdata;
+ config.driver_data = sreg;
+ config.of_node = pdev->dev.of_node;
+
+ /* register regulator */
+ rdev = regulator_register(rdesc, &config);
+ if (IS_ERR(rdev)) {
+ dev_err(dev, "failed to register %s\n",
+ rdesc->name);
+ ret = PTR_ERR(rdev);
+ goto hi6421_probe_end;
+ }
+
+ platform_set_drvdata(pdev, rdev);
+
+hi6421_probe_end:
+ return ret;
+}
+
+static int hi6421_regulator_remove(struct platform_device *pdev)
+{
+ struct regulator_dev *rdev = platform_get_drvdata(pdev);
+ struct hi6421_regulator *sreg = rdev_get_drvdata(rdev);
+
+ regulator_unregister(rdev);
+
+ return 0;
+}
+
+static struct platform_driver hi6421_regulator_driver = {
+ .driver = {
+ .name = "hi6421_regulator",
+ .owner = THIS_MODULE,
+ .of_match_table = of_hi6421_regulator_match_tbl,
+ },
+ .probe = hi6421_regulator_probe,
+ .remove = hi6421_regulator_remove,
+};
+module_platform_driver(hi6421_regulator_driver);
+
+MODULE_AUTHOR("Guodong Xu <guodong.xu@linaro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
diff --git a/include/linux/mfd/hi6421-pmic.h b/include/linux/mfd/hi6421-pmic.h
index 2f875cf52ae9..0b60a9b2a666 100644
--- a/include/linux/mfd/hi6421-pmic.h
+++ b/include/linux/mfd/hi6421-pmic.h
@@ -24,6 +24,8 @@
#define __HI6421_PMIC_H
#include <linux/irqdomain.h>
+#include <linux/mutex.h>
+#include <linux/time.h>
#define OCP_DEB_CTRL_REG (0x51)
#define OCP_DEB_SEL_MASK (0x0C)
@@ -66,6 +68,17 @@
#define HI6421_IRQ_BATTERY_ON 20 /* Battery inserted */
#define HI6421_IRQ_RESET 21 /* Reset */
+/**
+ * struct hi6421_pmic - Hi6421 PMIC chip-level data structure.
+ *
+ * This struct describes Hi6421 PMIC chip-level data.
+ *
+ * @enable_mutex: Used by regulators, to make sure that only one regulator is
+ * in the turning-on phase at any time. See also @last_enabled
+ * and hi6421_regulator_enable().
+ * @last_enabled: Used by regulators, to make sure enough distance in time
+ * between enabling of any of two regulators.
+ */
struct hi6421_pmic {
struct resource *res;
struct device *dev;
@@ -74,6 +87,8 @@ struct hi6421_pmic {
struct irq_domain *domain;
int irq;
int gpio;
+ struct mutex enable_mutex;
+ struct timeval last_enabled;
};
/* Register Access Helpers */