lkml.org 
[lkml]   [2013]   [Jun]   [4]   [last100]   RSS Feed
Views: [wrap][no wrap]   [headers]  [forward] 
 
Messages in this thread
    Patch in this message
    /
    From
    Subject[PATCH 2/3] regulator: add driver for hi6421 voltage regulator
    Date
    Add driver support for HiSilicon Hi6421 voltage regulators.

    Signed-off-by: Guodong Xu <guodong.xu@linaro.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@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 <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 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@linaro.org>");
    +MODULE_DESCRIPTION("Hi6421 regulator driver");
    +MODULE_LICENSE("GPL v2");
    --
    1.7.4.1


    \
     
     \ /
      Last update: 2013-06-04 19:41    [W:4.114 / U:0.448 seconds]
    ©2003-2020 Jasper Spaans|hosted at Digital Ocean and TransIP|Read the blog|Advertise on this site