From patchwork Sat Nov 26 18:13:25 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Nicolae Rosia X-Patchwork-Id: 9448479 Return-Path: Received: from mail.wl.linuxfoundation.org (pdx-wl-mail.web.codeaurora.org [172.30.200.125]) by pdx-korg-patchwork.web.codeaurora.org (Postfix) with ESMTP id 125466071C for ; Sat, 26 Nov 2016 18:17:43 +0000 (UTC) Received: from mail.wl.linuxfoundation.org (localhost [127.0.0.1]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id 059FA26419 for ; Sat, 26 Nov 2016 18:17:43 +0000 (UTC) Received: by mail.wl.linuxfoundation.org (Postfix, from userid 486) id EE91926C9B; Sat, 26 Nov 2016 18:17:42 +0000 (UTC) X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on pdx-wl-mail.web.codeaurora.org X-Spam-Level: X-Spam-Status: No, score=-6.9 required=2.0 tests=BAYES_00,RCVD_IN_DNSWL_HI autolearn=unavailable version=3.3.1 Received: from vger.kernel.org (vger.kernel.org [209.132.180.67]) by mail.wl.linuxfoundation.org (Postfix) with ESMTP id D581C26419 for ; Sat, 26 Nov 2016 18:17:41 +0000 (UTC) Received: (majordomo@vger.kernel.org) by vger.kernel.org via listexpand id S1753963AbcKZSOc (ORCPT ); Sat, 26 Nov 2016 13:14:32 -0500 Received: from relay1.mentorg.com ([192.94.38.131]:33190 "EHLO relay1.mentorg.com" rhost-flags-OK-OK-OK-OK) by vger.kernel.org with ESMTP id S1753947AbcKZSOW (ORCPT ); Sat, 26 Nov 2016 13:14:22 -0500 Received: from nat-ies.mentorg.com ([192.94.31.2] helo=SVR-IES-FEM-02.mgc.mentorg.com) by relay1.mentorg.com with esmtp id 1cAhUS-0003NU-C1 from Nicolae_Rosia@mentor.com ; Sat, 26 Nov 2016 10:14:21 -0800 Received: from rosia.mgc (137.202.0.76) by SVR-IES-FEM-02.mgc.mentorg.com (137.202.0.106) with Microsoft SMTP Server id 14.3.224.2; Sat, 26 Nov 2016 18:14:19 +0000 From: Nicolae Rosia To: Lee Jones , Mark Brown , Rob Herring , Mark Rutland , Tony Lindgren CC: Liam Girdwood , Paul Gortmaker , Graeme Gregory , Baruch Siach , , , , , Nicolae Rosia Subject: [PATCH 4/5] regulator: Add support for TI TWL6032 Date: Sat, 26 Nov 2016 20:13:25 +0200 Message-ID: <20161126181326.14951-5-Nicolae_Rosia@mentor.com> X-Mailer: git-send-email 2.9.3 In-Reply-To: <20161126181326.14951-1-Nicolae_Rosia@mentor.com> References: <20161126181326.14951-1-Nicolae_Rosia@mentor.com> MIME-Version: 1.0 Sender: linux-omap-owner@vger.kernel.org Precedence: bulk List-ID: X-Mailing-List: linux-omap@vger.kernel.org X-Virus-Scanned: ClamAV using ClamSMTP The TWL6032 PMIC is similar to TWL6030, has different output names, and regulator control logic. It is used on Barnes & Noble Nook HD and HD+. Signed-off-by: Nicolae Rosia --- .../bindings/regulator/twl6032-regulator.txt | 109 ++++ drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/twl6032-regulator.c | 582 +++++++++++++++++++++ 4 files changed, 699 insertions(+) create mode 100644 Documentation/devicetree/bindings/regulator/twl6032-regulator.txt create mode 100644 drivers/regulator/twl6032-regulator.c diff --git a/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt b/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt new file mode 100644 index 0000000..323f5a9 --- /dev/null +++ b/Documentation/devicetree/bindings/regulator/twl6032-regulator.txt @@ -0,0 +1,109 @@ +TWL6032 PMIC Voltage Regulator Bindings + +The parent node must be MFD TWL Core, ti,twl6032. + +Required properties: +- compatible: "ti,twl6032" + +Optional properties: +- regulators node containing regulator childs. + +The child regulators must be named after their hardware +counterparts: LDO[1-6], LDOLN, LDOUSB and VANA. + +Each regulator is defined using the standard binding +for regulators as described in ./regulator.txt + +Example: +twl { + compatible = "ti,twl6032"; + + [...] + + pmic { + compatible = "ti,twl6032-regulator"; + + regulators { + ldo1: LDO1 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <2500000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo2: LDO2 { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo3: LDO3 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo4: LDO4 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo5: LDO5 { + regulator-min-microvolt = <1200000>; + regulator-max-microvolt = <3000000>; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo6: LDO6 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-always-on; + + regulator-state-mem { + regulator-off-in-suspend; + }; + }; + + ldo7: LDO7 { + regulator-min-microvolt = <1800000>; + regulator-max-microvolt = <1800000>; + regulator-boot-on; + regulator-always-on; + }; + + ldoln: LDOLN { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + }; + + ldousb: LDOUSB { + regulator-min-microvolt = <1000000>; + regulator-max-microvolt = <3000000>; + }; + + vana: VANA { + regulator-min-microvolt = <2100000>; + regulator-max-microvolt = <2100000>; + regulator-always-on; + }; + }; + }; + + [...] +}; diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 936f7cc..3168aba 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -843,6 +843,13 @@ config REGULATOR_TWL4030 This driver supports the voltage regulators provided by this family of companion chips. +config REGULATOR_TWL6032 + tristate "TI TWL6032 PMIC" + depends on TWL4030_CORE + depends on OF || COMPILE_TEST + help + This driver supports the Texas Instruments TWL6032 voltage regulator. + config REGULATOR_VEXPRESS tristate "Versatile Express regulators" depends on VEXPRESS_CONFIG diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 2142a5d..185a979 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -105,6 +105,7 @@ obj-$(CONFIG_REGULATOR_TPS65910) += tps65910-regulator.o obj-$(CONFIG_REGULATOR_TPS65912) += tps65912-regulator.o obj-$(CONFIG_REGULATOR_TPS80031) += tps80031-regulator.o obj-$(CONFIG_REGULATOR_TWL4030) += twl-regulator.o +obj-$(CONFIG_REGULATOR_TWL6032) += twl6032-regulator.o obj-$(CONFIG_REGULATOR_VEXPRESS) += vexpress-regulator.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-dcdc.o obj-$(CONFIG_REGULATOR_WM831X) += wm831x-isink.o diff --git a/drivers/regulator/twl6032-regulator.c b/drivers/regulator/twl6032-regulator.c new file mode 100644 index 0000000..70a0fdf --- /dev/null +++ b/drivers/regulator/twl6032-regulator.c @@ -0,0 +1,582 @@ +/* + * TWL6032 regulator driver + * Copyright (C) 2016 Nicolae Rosia + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* TWL6032 register offsets */ +#define TWL6032_VREG_TRANS 1 +#define TWL6032_VREG_STATE 2 +#define TWL6032_VREG_VOLTAGE 3 + +#define TWL6032_LDO_MIN_MV 1000 +#define TWL6032_LDO_MAX_MV 3300 + +/* TWL6030 LDO register values for CFG_TRANS */ +#define TWL6032_CFG_TRANS_STATE_MASK 0x03 +#define TWL6032_CFG_TRANS_STATE_OFF 0x00 +#define TWL6032_CFG_TRANS_STATE_AUTO 0x01 +#define TWL6032_CFG_TRANS_SLEEP_SHIFT 2 + +#define TWL6032_CFG_STATE_MASK 0x03 +#define TWL6032_CFG_STATE_OFF 0x00 +#define TWL6032_CFG_STATE_ON 0x01 +#define TWL6032_CFG_STATE_OFF2 0x02 +#define TWL6032_CFG_STATE_SLEEP 0x03 + +static const char *rdev_get_name(struct regulator_dev *rdev) +{ + if (rdev->constraints && rdev->constraints->name) + return rdev->constraints->name; + else if (rdev->desc->name) + return rdev->desc->name; + else + return ""; +} + +struct twl6032_regulator_info { + u8 base; + unsigned int min_mV; + struct regulator_desc desc; +}; + +struct twl6032_regulator { + struct twl6032_regulator_info *info; +}; + +static int twl6032_set_trans_state(struct regulator_dev *rdev, u8 shift, u8 val) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + unsigned int state; + u8 mask; + int ret; + + /* Read CFG_TRANS register of TWL6030 */ + ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_TRANS, + &state); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_read: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + mask = TWL6032_CFG_TRANS_STATE_MASK << shift; + val = (val << shift) & mask; + + /* If value is already set, no need to write to reg */ + if (val == (state & mask)) + return 0; + + state &= ~mask; + state |= val; + + return regmap_write(rdev->regmap, info->base + TWL6032_VREG_TRANS, + state); +} + +static int +twl6032_ldo_list_voltage(struct regulator_dev *rdev, unsigned int sel) +{ + int ret; + + switch (sel) { + case 0: + ret = 0; + break; + case 1 ... 24: + /* Linear mapping from 00000001 to 00011000: + * Absolute voltage value = 1.0 V + 0.1 V × (sel – 00000001) + */ + ret = (TWL6032_LDO_MIN_MV + 100 * (sel - 1)) * 1000; + break; + case 25 ... 30: + ret = -EINVAL; + break; + case 31: + ret = 2750000; + break; + default: + ret = -EINVAL; + } + + dev_dbg(&rdev->dev, "%s %s: sel: %d, mV: %d\n", rdev_get_name(rdev), + __func__, sel, ret); + + return ret; +} + +static int +twl6032_ldo_set_voltage_sel(struct regulator_dev *rdev, unsigned int sel) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + int ret; + + dev_dbg(&rdev->dev, "%s %s: sel: 0x%02X\n", rdev_get_name(rdev), + __func__, sel); + + ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_VOLTAGE, + sel); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_write: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + return 0; +} + +static int twl6032_ldo_get_voltage_sel(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + unsigned int val; + int ret; + + ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_VOLTAGE, + &val); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_read: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + dev_dbg(&rdev->dev, "%s %s: vsel: 0x%02X\n", rdev_get_name(rdev), + __func__, val); + + return val; +} + +static int twl6032_ldo_enable(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + int ret; + + dev_dbg(&rdev->dev, "%s %s\n", rdev_get_name(rdev), __func__); + + ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE, + TWL6032_CFG_STATE_ON); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_write: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + ret = twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT, + TWL6032_CFG_TRANS_STATE_AUTO); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: twl6032_set_trans_state: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + return 0; +} + +static int twl6032_ldo_disable(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + int ret; + + dev_dbg(&rdev->dev, "%s %s\n", rdev_get_name(rdev), __func__); + + ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE, + TWL6032_CFG_STATE_OFF); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_write: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + ret = twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT, + TWL6032_CFG_TRANS_STATE_OFF); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: twl6032_set_trans_state: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + return 0; +} + +static int twl6032_ldo_is_enabled(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + int ret; + unsigned int val; + + ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_STATE, &val); + if (ret < 0) { + dev_err(&rdev->dev, "%s regmap_read: %d\n", __func__, ret); + return ret; + } + + dev_dbg(&rdev->dev, "%s %s: val: 0x%02X, val-masked: 0x%02X, ret: %d\n", + rdev_get_name(rdev), __func__, + val, val & TWL6032_CFG_STATE_MASK, + (val & TWL6032_CFG_STATE_MASK) == TWL6032_CFG_STATE_ON); + + val &= TWL6032_CFG_STATE_MASK; + + return val == TWL6032_CFG_STATE_ON; +} + +static int twl6032_ldo_set_mode(struct regulator_dev *rdev, unsigned int mode) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + unsigned int val = 0; + int ret; + + dev_dbg(&rdev->dev, "%s %s: mode: 0x%02X\n", rdev_get_name(rdev), + __func__, mode); + + switch (mode) { + case REGULATOR_MODE_NORMAL: + val |= TWL6032_CFG_STATE_ON; + break; + case REGULATOR_MODE_STANDBY: + val |= TWL6032_CFG_STATE_SLEEP; + break; + + default: + return -EINVAL; + } + + ret = regmap_write(rdev->regmap, info->base + TWL6032_VREG_STATE, val); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_write: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + return 0; +} + +static int twl6032_ldo_get_status(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + int ret; + unsigned int val; + + ret = regmap_read(rdev->regmap, info->base + TWL6032_VREG_STATE, &val); + if (ret < 0) { + dev_err(&rdev->dev, "%s %s: regmap_read: %d\n", + rdev_get_name(rdev), __func__, ret); + return ret; + } + + dev_dbg(&rdev->dev, "%s %s: val: 0x%02X, val-with-mask: 0x%02X\n", + rdev_get_name(rdev), __func__, + val, val & TWL6032_CFG_STATE_MASK); + + val &= TWL6032_CFG_STATE_MASK; + + switch (val) { + case TWL6032_CFG_STATE_ON: + return REGULATOR_STATUS_NORMAL; + + case TWL6032_CFG_STATE_SLEEP: + return REGULATOR_STATUS_STANDBY; + + case TWL6032_CFG_STATE_OFF: + case TWL6032_CFG_STATE_OFF2: + default: + break; + } + + return REGULATOR_STATUS_OFF; +} + +static int twl6032_ldo_suspend_enable(struct regulator_dev *rdev) +{ + return twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT, + TWL6032_CFG_TRANS_STATE_AUTO); +} + +static int twl6032_ldo_suspend_disable(struct regulator_dev *rdev) +{ + return twl6032_set_trans_state(rdev, TWL6032_CFG_TRANS_SLEEP_SHIFT, + TWL6032_CFG_TRANS_STATE_OFF); +} + +static int +twl6032_fixed_list_voltage(struct regulator_dev *rdev, unsigned int sel) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + + return info->min_mV * 1000; /* mV to V */ +} + +static int twl6032_fixed_get_voltage(struct regulator_dev *rdev) +{ + struct twl6032_regulator *twl6032_reg = rdev_get_drvdata(rdev); + struct twl6032_regulator_info *info = twl6032_reg->info; + + return info->min_mV * 1000; /* mV to V */ +} + +static const struct regulator_ops twl6032_ldo_ops = { + .list_voltage = twl6032_ldo_list_voltage, + .set_voltage_sel = twl6032_ldo_set_voltage_sel, + .get_voltage_sel = twl6032_ldo_get_voltage_sel, + .enable = twl6032_ldo_enable, + .disable = twl6032_ldo_disable, + .is_enabled = twl6032_ldo_is_enabled, + .set_mode = twl6032_ldo_set_mode, + .get_status = twl6032_ldo_get_status, + .set_suspend_enable = twl6032_ldo_suspend_enable, + .set_suspend_disable = twl6032_ldo_suspend_disable, +}; + +static const struct regulator_ops twl6032_fixed_ops = { + .list_voltage = twl6032_fixed_list_voltage, + .get_voltage = twl6032_fixed_get_voltage, + .enable = twl6032_ldo_enable, + .disable = twl6032_ldo_disable, + .is_enabled = twl6032_ldo_is_enabled, + .set_mode = twl6032_ldo_set_mode, + .get_status = twl6032_ldo_get_status, + .set_suspend_enable = twl6032_ldo_suspend_enable, + .set_suspend_disable = twl6032_ldo_suspend_disable, +}; + +#define TWL6032_LDO_REG_VOLTAGES \ + ((TWL6032_LDO_MAX_MV - TWL6032_LDO_MIN_MV) / 100 + 1) +#define TWL6032_LDO_REG(_id, _reg) \ +{ \ + .base = _reg, \ + .desc = { \ + .name = "twl6032-reg-" # _id, \ + .n_voltages = TWL6032_LDO_REG_VOLTAGES, \ + .ops = &twl6032_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +#define TWL6032_FIXED_REG(_id, _reg, _min_mV) \ +{ \ + .base = _reg, \ + .min_mV = _min_mV, \ + .desc = { \ + .name = "twl6032-reg-" # _id, \ + .n_voltages = 1, \ + .ops = &twl6032_fixed_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +#define TWL6032_RESOURCE_REG(_id, _reg) \ +{ \ + .base = _reg, \ + .desc = { \ + .name = "twl6032-reg-" # _id, \ + .ops = &twl6032_ldo_ops, \ + .type = REGULATOR_VOLTAGE, \ + .owner = THIS_MODULE, \ + }, \ +} + +static struct twl6032_regulator_info twl6032_ldo_reg_info[] = { + TWL6032_LDO_REG(LDO1, 0x9C), + TWL6032_LDO_REG(LDO2, 0x84), + TWL6032_LDO_REG(LDO3, 0x8C), + TWL6032_LDO_REG(LDO4, 0x88), + TWL6032_LDO_REG(LDO5, 0x98), + TWL6032_LDO_REG(LDO6, 0x90), + TWL6032_LDO_REG(LDO7, 0xA4), + TWL6032_LDO_REG(LDOLN, 0x94), + TWL6032_LDO_REG(LDOUSB, 0xA0), +}; + +static struct twl6032_regulator_info twl6032_fixed_reg_info[] = { + TWL6032_FIXED_REG(VANA, 0x80, 2100), +}; + +static struct of_regulator_match +twl6032_ldo_reg_matches[] = { + { .name = "LDO1", }, + { .name = "LDO2", }, + { .name = "LDO3", }, + { .name = "LDO4", }, + { .name = "LDO5", }, + { .name = "LDO6", }, + { .name = "LDO7", }, + { .name = "LDOLN" }, + { .name = "LDOUSB" } +}; + +static struct of_regulator_match +twl6032_fixed_reg_matches[] = { + { .name = "VANA", }, +}; + +#define TWL6032_LDO_REG_NUM ARRAY_SIZE(twl6032_ldo_reg_matches) +#define TWL6032_FIXED_REG_NUM ARRAY_SIZE(twl6032_fixed_reg_matches) + +struct twl6032_regulator_priv { + struct twl6032_regulator ldo_regulators[TWL6032_LDO_REG_NUM]; + struct twl6032_regulator fixed_regulators[TWL6032_FIXED_REG_NUM]; +}; + +static int twl6032_regulator_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *regulators; + struct of_regulator_match *match; + struct twlcore *twl = dev_get_drvdata(pdev->dev.parent); + struct twl6032_regulator_priv *priv; + struct regulator_config config = { + .dev = &pdev->dev, + }; + struct regulator_dev *rdev; + int ret, i; + + if (!dev->of_node) { + dev_err(&pdev->dev, "no DT info\n"); + return -EINVAL; + } + + regulators = of_get_child_by_name(dev->of_node, "regulators"); + if (!regulators) { + dev_err(dev, "regulator node not found\n"); + return -EINVAL; + } + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + platform_set_drvdata(pdev, priv); + + /* LDO regulators parsing */ + ret = of_regulator_match(dev, regulators, twl6032_ldo_reg_matches, + TWL6032_LDO_REG_NUM); + of_node_put(regulators); + if (ret < 0) { + dev_err(dev, "error parsing LDO reg init data: %d\n", ret); + return ret; + } + + for (i = 0; i < TWL6032_LDO_REG_NUM; i++) { + struct twl6032_regulator *twl6032_reg; + + match = &twl6032_ldo_reg_matches[i]; + if (!match->of_node) + continue; + + twl6032_reg = &priv->ldo_regulators[i]; + twl6032_reg->info = &twl6032_ldo_reg_info[i]; + + config.init_data = match->init_data; + config.driver_data = &priv->ldo_regulators[i]; + config.regmap = twl->twl_modules[0].regmap; + config.of_node = match->of_node; + + rdev = devm_regulator_register(dev, &twl6032_reg->info->desc, + &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "failed to register regulator %s: %d\n", + twl6032_reg->info->desc.name, ret); + return ret; + } + } + + /* Fixed regulators parsing */ + ret = of_regulator_match(dev, regulators, twl6032_fixed_reg_matches, + TWL6032_FIXED_REG_NUM); + of_node_put(regulators); + if (ret < 0) { + dev_err(dev, "error parsing fixed reg init data: %d\n", ret); + return ret; + } + + for (i = 0; i < TWL6032_FIXED_REG_NUM; i++) { + struct twl6032_regulator *twl6032_reg; + + match = &twl6032_fixed_reg_matches[i]; + if (!match->of_node) + continue; + + twl6032_reg = &priv->fixed_regulators[i]; + twl6032_reg->info = &twl6032_fixed_reg_info[i]; + + config.init_data = match->init_data; + config.driver_data = &priv->fixed_regulators[i]; + config.regmap = twl->twl_modules[0].regmap; + config.of_node = match->of_node; + + rdev = devm_regulator_register(dev, &twl6032_reg->info->desc, + &config); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); + dev_err(dev, "failed to register regulator %s: %d\n", + twl6032_reg->info->desc.name, ret); + return ret; + } + } + + return 0; +} + +static int twl6032_regulator_remove(struct platform_device *pdev) +{ + return 0; +} + +static const struct of_device_id twl6032_dt_match[] = { + { .compatible = "ti,twl6032-regulator" }, + { /* last entry */ } +}; + +MODULE_DEVICE_TABLE(platform, twl6032_regulator_driver_ids); + +static struct platform_driver twl6032_regulator_driver = { + .driver = { + .name = "twl6032-regulator", + .of_match_table = twl6032_dt_match, + }, + .probe = twl6032_regulator_probe, + .remove = twl6032_regulator_remove, +}; + +static int __init twl6032_regulator_init(void) +{ + return platform_driver_register(&twl6032_regulator_driver); +} +subsys_initcall(twl6032_regulator_init); + +static void __exit twl6032_regulator_exit(void) +{ + platform_driver_unregister(&twl6032_regulator_driver); +} +module_exit(twl6032_regulator_exit); + +MODULE_AUTHOR("Nicolae Rosia "); +MODULE_DESCRIPTION("TI TWL6032 Regulator Driver"); +MODULE_LICENSE("GPL v2");