lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [thread-next>] [day] [month] [year] [list]
Message-Id: <1370356123-22357-3-git-send-email-guodong.xu@linaro.org>
Date:	Tue,  4 Jun 2013 22:28:42 +0800
From:	Guodong Xu <guodong.xu@...aro.org>
To:	sameo@...ux.intel.com, lgirdwood@...il.com, broonie@...nel.org
Cc:	linux-kernel@...r.kernel.org, patches@...aro.org,
	guodong.xu@...aro.org
Subject: [PATCH 2/3] regulator: add driver for hi6421 voltage regulator

Add driver support for HiSilicon Hi6421 voltage regulators.

Signed-off-by: Guodong Xu <guodong.xu@...aro.org>
---
 .../bindings/regulator/hi6421-regulator.txt        |   82 +++
 drivers/regulator/Kconfig                          |    8 +-
 drivers/regulator/Makefile                         |    2 +-
 drivers/regulator/hi6421-regulator.c               |  559 ++++++++++++++++++++
 4 files changed, 649 insertions(+), 2 deletions(-)
 create mode 100644 Documentation/devicetree/bindings/regulator/hi6421-regulator.txt
 create mode 100644 drivers/regulator/hi6421-regulator.c

diff --git a/Documentation/devicetree/bindings/regulator/hi6421-regulator.txt b/Documentation/devicetree/bindings/regulator/hi6421-regulator.txt
new file mode 100644
index 0000000..165dc26
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/hi6421-regulator.txt
@@ -0,0 +1,82 @@
+Hi6421 regulator
+
+Hi6421 is a power management IC designed by HiSilicon Technologies Co., Ltd. It
+has functionalities of the following:
+- Power Management Unit, including regulators
+- Audio processing, codecs
+- Misc functions, such as LEDs, RTC, etc.
+
+In structure, Hi6421 device tree node is devided into two levels, each has its
+own compatible field.
+
+In its first level, hi6421 chip level properties are defined, such as reg,
+interrupt, gpios.
+
+In its second level, sub-component level of properties are defined. For example,
+in case of LDO regulators, there are regulator-name, regulator-min-microvolt
+properties; in case of rtc, there are interrupts property.
+
+This document describes devicetree binding (second level) information about
+Hi6421 regulators.
+
+Required properties:
+- compatible: three types of regulator are defined,
+	- "hisilicon,hi6421-ldo"
+	- "hisilicon,hi6421-buck012"
+	- "hisilicon,hi6421-buck345"
+- hisilicon,hi6421-ctrl: <ctrl_reg enable_mask eco_mode_mask>
+	- ctrl_reg: control register offset address
+	- enable_mask: regulator on/off bitmask
+	- eco_mode_mask: ECO mode on/off bitmask
+- hisilicon,hi6421-vset: <vset_reg vset_mask>
+	- vset_reg: voltage set register offset address
+	- vset_mask: voltage setting bitmask
+- hisilicon,hi6421-n-voltages: <n>
+	- n: number of voltage levels supported
+- hisilicon,hi6421-vset-table: array of voltages selectable in this regulator
+			       in unit of microvolt
+	Note: 1) size of this array equals <n> in "hisilicon,hi6421-n-voltages"
+	      2) this and next property "hisilicon,hi6421-uv-step" cannot be
+	      set at the same time for the same regulator node.
+- hisilicon,hi6421-uv-step: <uV_step>
+	- uV_step: step (in uV) for a linear mapping between selector and
+		   result voltage
+- hisilicon,hi6421-off-on-delay-us: <off_on_delay>
+	- off_on_delay: guard time (in microseconds), before re-enabling
+		        a previously disabled regulator
+- hisilicon,hi6421-enable-time-us: <enable_time>
+	- enable_time: time taken for initial enable of regulator (in uS)
+
+Properties defined by the standard binding for regulators: (See regulator.txt)
+- regulator-name:
+- regulator-min-microvolt:
+- regulator-max-microvolt:
+- regulator-boot-on:
+- regulator-always-on:
+
+Optional properties:
+- hisilicon,hi6421-eco-microamp: maximum current allowed in ECO mode (in uA)
+
+Example:
+
+		pmic: pmic@...00000 {
+			compatible = "hisilicon,hi6421-pmic";
+			reg = <0xfcc00000 0x0180>; /* 0x60 << 2 */
+
+			ldo0: ldo@20 {
+				compatible = "hisilicon,hi6421-ldo";
+				regulator-name = "LDO0";
+				regulator-min-microvolt = <2850000>;
+				regulator-max-microvolt = <2850000>;
+				hisilicon,hi6421-ctrl = <0x20 0x10 0x20>;
+				hisilicon,hi6421-vset = <0x20 0x07>;
+				hisilicon,hi6421-n-voltages = <8>;
+				hisilicon,hi6421-vset-table = <1500000>, <1800000>,
+							      <2400000>, <2500000>,
+							      <2600000>, <2700000>,
+							      <2850000>, <3000000>;
+				hisilicon,hi6421-off-on-delay-us = <10000>;
+				hisilicon,hi6421-enable-time-us = <250>;
+				hisilicon,hi6421-eco-microamp = <8000>;
+			};
+		};
diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig
index 8bb2644..616254c 100644
--- a/drivers/regulator/Kconfig
+++ b/drivers/regulator/Kconfig
@@ -514,5 +514,11 @@ config REGULATOR_AS3711
 	  This driver provides support for the voltage regulators on the
 	  AS3711 PMIC
 
-endif
+config REGULATOR_HI6421
+	tristate "HiSilicon Hi6421 PMIC voltage regulator support"
+	depends on MFD_HI6421_PMIC && OF
+	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 47a34ff..5a67225 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 0000000..62fd2df
--- /dev/null
+++ b/drivers/regulator/hi6421-regulator.c
@@ -0,0 +1,559 @@
+/*
+ * Device driver for regulators in Hi6421 IC
+ *
+ * Copyright (c) <2011-2013> HiSilicon Technologies Co., Ltd.
+ *              http://www.hisilicon.com
+ * Copyright (c) <2011-2013> Linaro Ltd.
+ *              http://www.linaro.org
+ *
+ * Author: Guodong Xu <guodong.xu@...aro.org>
+ *
+ * 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.
+ *
+ * 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 DEFINE_MUTEX(enable_mutex);
+struct timeval last_enabled;
+
+
+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(&enable_mutex);
+	ensured_time_after(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(&last_enabled);
+	mutex_unlock(&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_end1;
+	}
+	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_end1;
+	}
+
+dt_parse_ldo_end1:
+	devm_kfree(dev, v_table);
+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 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);
+	sreg = kmemdup(template, sizeof(*sreg), GFP_KERNEL);
+	if (!sreg)
+		return -ENOMEM;
+
+	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:
+	if (ret)
+		kfree(sreg);
+	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);
+	kfree(sreg);
+	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@...aro.org>");
+MODULE_DESCRIPTION("Hi6421 regulator driver");
+MODULE_LICENSE("GPL v2");
-- 
1.7.4.1

--
To unsubscribe from this list: send the line "unsubscribe linux-kernel" in
the body of a message to majordomo@...r.kernel.org
More majordomo info at  http://vger.kernel.org/majordomo-info.html
Please read the FAQ at  http://www.tux.org/lkml/

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ