From patchwork Tue Jun 4 14:28:42 2013 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Guodong Xu X-Patchwork-Id: 17521 Return-Path: X-Original-To: linaro@patches.linaro.org Delivered-To: linaro@patches.linaro.org Received: from mail-ve0-f198.google.com (mail-ve0-f198.google.com [209.85.128.198]) by ip-10-151-82-157.ec2.internal (Postfix) with ESMTPS id 4E1D925A2B for ; Tue, 4 Jun 2013 14:27:03 +0000 (UTC) Received: by mail-ve0-f198.google.com with SMTP id jz10sf329259veb.9 for ; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=20120113; h=mime-version:x-beenthere:x-forwarded-to:x-forwarded-for :delivered-to:from:to:cc:subject:date:message-id:x-mailer :in-reply-to:references:x-gm-message-state:x-original-sender :x-original-authentication-results:precedence:mailing-list:list-id :x-google-group-id:list-post:list-help:list-archive:list-unsubscribe; bh=FD6jmt7Tkdu4iEnK3YnA01kLiBlLid1vLL0lZAUSolM=; b=bZrYLA3HQnIqkbmc0vzqql6IGtwmLBJMWQV+T7QmAEHOg1BMg8SCwFbvn0YVNaVMLU LVcnlob9LLOLPn3hAP1JJHwhwQgqQ8hwFpsM+37EhspHEaglRUJ9nRal/FLEfxU6m19K fdRxezJaG5cj908M7kA3ogdh/47tTdUvXXtJfijPfiYZ8wQgP6hcFwemjxDYrnHt1VgY tEAUMFRxSndD8P8X/pjiP1dmbJ+JiXMcOF+jj0btXcAC93XcWydrbwqB9YCe222o4HbJ 9V8dPPU+k5YCcWUktwWd68gEZ3hvqjuogOcPHBO5NYYtEYyLh3ADsmqJXUdJojjYJ3g/ CJbw== X-Received: by 10.224.36.66 with SMTP id s2mr16583147qad.6.1370356022865; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) MIME-Version: 1.0 X-BeenThere: patchwork-forward@linaro.org Received: by 10.49.42.106 with SMTP id n10ls294949qel.90.gmail; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) X-Received: by 10.58.116.229 with SMTP id jz5mr19036073veb.14.1370356022663; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) Received: from mail-vc0-f175.google.com (mail-vc0-f175.google.com [209.85.220.175]) by mx.google.com with ESMTPS id ha8si37682993vdb.100.2013.06.04.07.27.02 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 04 Jun 2013 07:27:02 -0700 (PDT) Received-SPF: neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) client-ip=209.85.220.175; Received: by mail-vc0-f175.google.com with SMTP id hr11so209415vcb.20 for ; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) X-Received: by 10.220.246.8 with SMTP id lw8mr18514431vcb.8.1370356022391; Tue, 04 Jun 2013 07:27:02 -0700 (PDT) X-Forwarded-To: patchwork-forward@linaro.org X-Forwarded-For: patch@linaro.org patchwork-forward@linaro.org Delivered-To: patches@linaro.org Received: by 10.221.10.206 with SMTP id pb14csp128126vcb; Tue, 4 Jun 2013 07:27:01 -0700 (PDT) X-Received: by 10.68.232.194 with SMTP id tq2mr28973385pbc.133.1370356021250; Tue, 04 Jun 2013 07:27:01 -0700 (PDT) Received: from mail-pb0-x231.google.com (mail-pb0-x231.google.com [2607:f8b0:400e:c01::231]) by mx.google.com with ESMTPS id rl1si15122536pbc.84.2013.06.04.07.27.00 for (version=TLSv1 cipher=ECDHE-RSA-RC4-SHA bits=128/128); Tue, 04 Jun 2013 07:27:01 -0700 (PDT) Received-SPF: neutral (google.com: 2607:f8b0:400e:c01::231 is neither permitted nor denied by best guess record for domain of guodong.xu@linaro.org) client-ip=2607:f8b0:400e:c01::231; Received: by mail-pb0-f49.google.com with SMTP id jt11so316488pbb.8 for ; Tue, 04 Jun 2013 07:27:00 -0700 (PDT) X-Received: by 10.66.222.69 with SMTP id qk5mr29174766pac.71.1370356020738; Tue, 04 Jun 2013 07:27:00 -0700 (PDT) Received: from localhost.localdomain ([125.39.108.104]) by mx.google.com with ESMTPSA id cp1sm63504906pbc.42.2013.06.04.07.26.56 for (version=TLSv1 cipher=RC4-SHA bits=128/128); Tue, 04 Jun 2013 07:26:59 -0700 (PDT) From: Guodong Xu To: sameo@linux.intel.com, lgirdwood@gmail.com, broonie@kernel.org Cc: linux-kernel@vger.kernel.org, patches@linaro.org, guodong.xu@linaro.org Subject: [PATCH 2/3] regulator: add driver for hi6421 voltage regulator Date: Tue, 4 Jun 2013 22:28:42 +0800 Message-Id: <1370356123-22357-3-git-send-email-guodong.xu@linaro.org> X-Mailer: git-send-email 1.7.4.1 In-Reply-To: <1370356123-22357-1-git-send-email-guodong.xu@linaro.org> References: <1370356123-22357-1-git-send-email-guodong.xu@linaro.org> X-Gm-Message-State: ALoCoQmDBZ6DuTG0xh3zNczNVN+c2m/OVXYW8pZnhB1SJ3pXWuXPqRkKXTubOs3AWDBBe2Unzd4t X-Original-Sender: guodong.xu@linaro.org X-Original-Authentication-Results: mx.google.com; spf=neutral (google.com: 209.85.220.175 is neither permitted nor denied by best guess record for domain of patch+caf_=patchwork-forward=linaro.org@linaro.org) smtp.mail=patch+caf_=patchwork-forward=linaro.org@linaro.org Precedence: list Mailing-list: list patchwork-forward@linaro.org; contact patchwork-forward+owners@linaro.org List-ID: X-Google-Group-Id: 836684582541 List-Post: , List-Help: , List-Archive: List-Unsubscribe: , Add driver support for HiSilicon Hi6421 voltage regulators. Signed-off-by: Guodong Xu --- .../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: control register offset address + - enable_mask: regulator on/off bitmask + - eco_mode_mask: ECO mode on/off bitmask +- hisilicon,hi6421-vset: + - vset_reg: voltage set register offset address + - vset_mask: voltage setting bitmask +- hisilicon,hi6421-n-voltages: + - 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 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: step (in uV) for a linear mapping between selector and + result voltage +- hisilicon,hi6421-off-on-delay-us: + - off_on_delay: guard time (in microseconds), before re-enabling + a previously disabled regulator +- hisilicon,hi6421-enable-time-us: + - 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@fcc00000 { + 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_DESCRIPTION("Hi6421 regulator driver"); +MODULE_LICENSE("GPL v2");