From dfe7a1b058bbb29fa524ca7cf05aa13ff52983f4 Mon Sep 17 00:00:00 2001 From: Carlo Caione Date: Fri, 11 Apr 2014 11:38:10 +0200 Subject: regulator: AXP20x: Add support for regulators subsystem AXP202 and AXP209 come with two synchronous step-down DC-DCs and five LDOs. This patch introduces basic support for those regulators. Signed-off-by: Carlo Caione Signed-off-by: Mark Brown --- drivers/regulator/Kconfig | 7 + drivers/regulator/Makefile | 1 + drivers/regulator/axp20x-regulator.c | 285 +++++++++++++++++++++++++++++++++++ 3 files changed, 293 insertions(+) create mode 100644 drivers/regulator/axp20x-regulator.c (limited to 'drivers/regulator') diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 903eb37f047a..65e5d7d1b35a 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -139,6 +139,13 @@ config REGULATOR_AS3722 AS3722 PMIC. This will enable support for all the software controllable DCDC/LDO regulators. +config REGULATOR_AXP20X + tristate "X-POWERS AXP20X PMIC Regulators" + depends on MFD_AXP20X + help + This driver provides support for the voltage regulators on the + AXP20X PMIC. + config REGULATOR_BCM590XX tristate "Broadcom BCM590xx PMU Regulators" depends on MFD_BCM590XX diff --git a/drivers/regulator/Makefile b/drivers/regulator/Makefile index 12ef277a48b4..c14696b290c0 100644 --- a/drivers/regulator/Makefile +++ b/drivers/regulator/Makefile @@ -20,6 +20,7 @@ obj-$(CONFIG_REGULATOR_ANATOP) += anatop-regulator.o obj-$(CONFIG_REGULATOR_ARIZONA) += arizona-micsupp.o arizona-ldo1.o obj-$(CONFIG_REGULATOR_AS3711) += as3711-regulator.o obj-$(CONFIG_REGULATOR_AS3722) += as3722-regulator.o +obj-$(CONFIG_REGULATOR_AXP20X) += axp20x-regulator.o obj-$(CONFIG_REGULATOR_BCM590XX) += bcm590xx-regulator.o obj-$(CONFIG_REGULATOR_DA903X) += da903x.o obj-$(CONFIG_REGULATOR_DA9052) += da9052-regulator.o diff --git a/drivers/regulator/axp20x-regulator.c b/drivers/regulator/axp20x-regulator.c new file mode 100644 index 000000000000..78a29e60f53a --- /dev/null +++ b/drivers/regulator/axp20x-regulator.c @@ -0,0 +1,285 @@ +/* + * AXP20x regulators driver. + * + * Copyright (C) 2013 Carlo Caione + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#define AXP20X_IO_ENABLED 0x03 +#define AXP20X_IO_DISABLED 0x07 + +#define AXP20X_WORKMODE_DCDC2_MASK BIT(2) +#define AXP20X_WORKMODE_DCDC3_MASK BIT(1) + +#define AXP20X_FREQ_DCDC_MASK 0x0f + +#define AXP20X_DESC_IO(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ + _emask, _enable_val, _disable_val) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .enable_val = (_enable_val), \ + .disable_val = (_disable_val), \ + .ops = &axp20x_ops, \ + } + +#define AXP20X_DESC(_id, _supply, _min, _max, _step, _vreg, _vmask, _ereg, \ + _emask) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = (((_max) - (_min)) / (_step) + 1), \ + .owner = THIS_MODULE, \ + .min_uV = (_min) * 1000, \ + .uV_step = (_step) * 1000, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .ops = &axp20x_ops, \ + } + +#define AXP20X_DESC_FIXED(_id, _supply, _volt) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = 1, \ + .owner = THIS_MODULE, \ + .min_uV = (_volt) * 1000, \ + .ops = &axp20x_ops_fixed \ + } + +#define AXP20X_DESC_TABLE(_id, _supply, _table, _vreg, _vmask, _ereg, _emask) \ + [AXP20X_##_id] = { \ + .name = #_id, \ + .supply_name = (_supply), \ + .type = REGULATOR_VOLTAGE, \ + .id = AXP20X_##_id, \ + .n_voltages = ARRAY_SIZE(_table), \ + .owner = THIS_MODULE, \ + .vsel_reg = (_vreg), \ + .vsel_mask = (_vmask), \ + .enable_reg = (_ereg), \ + .enable_mask = (_emask), \ + .volt_table = (_table), \ + .ops = &axp20x_ops_table, \ + } + +static const int axp20x_ldo4_data[] = { 1250000, 1300000, 1400000, 1500000, 1600000, + 1700000, 1800000, 1900000, 2000000, 2500000, + 2700000, 2800000, 3000000, 3100000, 3200000, + 3300000 }; + +static struct regulator_ops axp20x_ops_fixed = { + .list_voltage = regulator_list_voltage_linear, +}; + +static struct regulator_ops axp20x_ops_table = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_table, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static struct regulator_ops axp20x_ops = { + .set_voltage_sel = regulator_set_voltage_sel_regmap, + .get_voltage_sel = regulator_get_voltage_sel_regmap, + .list_voltage = regulator_list_voltage_linear, + .enable = regulator_enable_regmap, + .disable = regulator_disable_regmap, + .is_enabled = regulator_is_enabled_regmap, +}; + +static const struct regulator_desc axp20x_regulators[] = { + AXP20X_DESC(DCDC2, "vin2", 700, 2275, 25, AXP20X_DCDC2_V_OUT, 0x3f, + AXP20X_PWR_OUT_CTRL, 0x10), + AXP20X_DESC(DCDC3, "vin3", 700, 3500, 25, AXP20X_DCDC3_V_OUT, 0x7f, + AXP20X_PWR_OUT_CTRL, 0x02), + AXP20X_DESC_FIXED(LDO1, "acin", 1300), + AXP20X_DESC(LDO2, "ldo24in", 1800, 3300, 100, AXP20X_LDO24_V_OUT, 0xf0, + AXP20X_PWR_OUT_CTRL, 0x04), + AXP20X_DESC(LDO3, "ldo3in", 700, 3500, 25, AXP20X_LDO3_V_OUT, 0x7f, + AXP20X_PWR_OUT_CTRL, 0x40), + AXP20X_DESC_TABLE(LDO4, "ldo24in", axp20x_ldo4_data, AXP20X_LDO24_V_OUT, 0x0f, + AXP20X_PWR_OUT_CTRL, 0x08), + AXP20X_DESC_IO(LDO5, "ldo5in", 1800, 3300, 100, AXP20X_LDO5_V_OUT, 0xf0, + AXP20X_GPIO0_CTRL, 0x07, AXP20X_IO_ENABLED, + AXP20X_IO_DISABLED), +}; + +#define AXP_MATCH(_name, _id) \ + [AXP20X_##_id] = { \ + .name = #_name, \ + .driver_data = (void *) &axp20x_regulators[AXP20X_##_id], \ + } + +static struct of_regulator_match axp20x_matches[] = { + AXP_MATCH(dcdc2, DCDC2), + AXP_MATCH(dcdc3, DCDC3), + AXP_MATCH(ldo1, LDO1), + AXP_MATCH(ldo2, LDO2), + AXP_MATCH(ldo3, LDO3), + AXP_MATCH(ldo4, LDO4), + AXP_MATCH(ldo5, LDO5), +}; + +static int axp20x_set_dcdc_freq(struct platform_device *pdev, u32 dcdcfreq) +{ + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + + if (dcdcfreq < 750) { + dcdcfreq = 750; + dev_warn(&pdev->dev, "DCDC frequency too low. Set to 750kHz\n"); + } + + if (dcdcfreq > 1875) { + dcdcfreq = 1875; + dev_warn(&pdev->dev, "DCDC frequency too high. Set to 1875kHz\n"); + } + + dcdcfreq = (dcdcfreq - 750) / 75; + + return regmap_update_bits(axp20x->regmap, AXP20X_DCDC_FREQ, + AXP20X_FREQ_DCDC_MASK, dcdcfreq); +} + +static int axp20x_regulator_parse_dt(struct platform_device *pdev) +{ + struct device_node *np, *regulators; + int ret; + u32 dcdcfreq; + + np = of_node_get(pdev->dev.parent->of_node); + if (!np) + return 0; + + regulators = of_find_node_by_name(np, "regulators"); + if (!regulators) { + dev_warn(&pdev->dev, "regulators node not found\n"); + } else { + ret = of_regulator_match(&pdev->dev, regulators, axp20x_matches, + ARRAY_SIZE(axp20x_matches)); + if (ret < 0) { + dev_err(&pdev->dev, "Error parsing regulator init data: %d\n", ret); + return ret; + } + + dcdcfreq = 1500; + of_property_read_u32(regulators, "x-powers,dcdc-freq", &dcdcfreq); + ret = axp20x_set_dcdc_freq(pdev, dcdcfreq); + if (ret < 0) { + dev_err(&pdev->dev, "Error setting dcdc frequency: %d\n", ret); + return ret; + } + + of_node_put(regulators); + } + + return 0; +} + +static int axp20x_set_dcdc_workmode(struct regulator_dev *rdev, int id, u32 workmode) +{ + unsigned int mask = AXP20X_WORKMODE_DCDC2_MASK; + + if ((id != AXP20X_DCDC2) && (id != AXP20X_DCDC3)) + return -EINVAL; + + if (id == AXP20X_DCDC3) + mask = AXP20X_WORKMODE_DCDC3_MASK; + + workmode <<= ffs(mask) - 1; + + return regmap_update_bits(rdev->regmap, AXP20X_DCDC_MODE, mask, workmode); +} + +static int axp20x_regulator_probe(struct platform_device *pdev) +{ + struct regulator_dev *rdev; + struct axp20x_dev *axp20x = dev_get_drvdata(pdev->dev.parent); + struct regulator_config config = { }; + struct regulator_init_data *init_data; + int ret, i; + u32 workmode; + + ret = axp20x_regulator_parse_dt(pdev); + if (ret) + return ret; + + for (i = 0; i < AXP20X_REG_ID_MAX; i++) { + init_data = axp20x_matches[i].init_data; + + config.dev = &pdev->dev; + config.init_data = init_data; + config.regmap = axp20x->regmap; + config.of_node = axp20x_matches[i].of_node; + + rdev = devm_regulator_register(&pdev->dev, &axp20x_regulators[i], + &config); + if (IS_ERR(rdev)) { + dev_err(&pdev->dev, "Failed to register %s\n", + axp20x_regulators[i].name); + + return PTR_ERR(rdev); + } + + ret = of_property_read_u32(axp20x_matches[i].of_node, "x-powers,dcdc-workmode", + &workmode); + if (!ret) { + if (axp20x_set_dcdc_workmode(rdev, i, workmode)) + dev_err(&pdev->dev, "Failed to set workmode on %s\n", + axp20x_regulators[i].name); + } + } + + return 0; +} + +static struct platform_driver axp20x_regulator_driver = { + .probe = axp20x_regulator_probe, + .driver = { + .name = "axp20x-regulator", + .owner = THIS_MODULE, + }, +}; + +module_platform_driver(axp20x_regulator_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_AUTHOR("Carlo Caione "); +MODULE_DESCRIPTION("Regulator Driver for AXP20X PMIC"); -- cgit v1.2.3 From bd0dda744cdfb93a1907091c4540764593a28fa2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 3 Apr 2014 15:32:15 +0100 Subject: regulator: core: Add of_node_get to of_regulator_match Currently, of_regulator_match does not increment the reference count of the of_nodes it takes new references to. This could cause the node pointer held to be invalid, by the time it is passed to the regulator core. This patchs adds an of_node_get when we copy each of_node pointer into the match structure. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index ea4f36f2cbe2..c58c8d178948 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -119,7 +119,8 @@ EXPORT_SYMBOL_GPL(of_get_regulator_init_data); * regulator. The data parsed from a child node will be matched to a regulator * based on either the deprecated property regulator-compatible if present, * or otherwise the child node's name. Note that the match table is modified - * in place. + * in place and an additional of_node reference is taken for each matched + * regulator. * * Returns the number of matches found or a negative error code on failure. */ @@ -162,7 +163,7 @@ int of_regulator_match(struct device *dev, struct device_node *node, child->name); return -EINVAL; } - match->of_node = child; + match->of_node = of_node_get(child); count++; break; } -- cgit v1.2.3 From 3764806440149ea9024dff039497d1e45d6ed027 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 3 Apr 2014 15:32:16 +0100 Subject: regulator: core: Add helper to put of_nodes from matches As of_regulator_match will take an of_node reference to each matched regulator, it makes sense to provide a helper to put all those references. This patch does that. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 21 +++++++++++++++++++++ include/linux/regulator/of_regulator.h | 7 +++++++ 2 files changed, 28 insertions(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index c58c8d178948..188e0cb10d03 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -172,3 +172,24 @@ int of_regulator_match(struct device *dev, struct device_node *node, return count; } EXPORT_SYMBOL_GPL(of_regulator_match); + +/** + * of_regulator_put_match - put the of_node references from an + * of_regulator_match structure + * @matches: match table for the regulators + * @num_matches: number of entries in match table + * + * This function goes through a match table and calls of_node_put on each + * of_node. + */ +int of_regulator_put_match(struct of_regulator_match *matches, + unsigned int num_matches) +{ + int i; + + for (i = 0; i < num_matches; i++) + of_node_put(matches[i].of_node); + + return 0; +} +EXPORT_SYMBOL_GPL(of_regulator_put_match); diff --git a/include/linux/regulator/of_regulator.h b/include/linux/regulator/of_regulator.h index f9217965aaa3..06528516aa15 100644 --- a/include/linux/regulator/of_regulator.h +++ b/include/linux/regulator/of_regulator.h @@ -20,6 +20,8 @@ extern struct regulator_init_data extern int of_regulator_match(struct device *dev, struct device_node *node, struct of_regulator_match *matches, unsigned int num_matches); +extern int of_regulator_put_match(struct of_regulator_match *matches, + unsigned int num_matches); #else static inline struct regulator_init_data *of_get_regulator_init_data(struct device *dev, @@ -35,6 +37,11 @@ static inline int of_regulator_match(struct device *dev, { return 0; } +static inline int of_regulator_put_match(struct of_regulator_match *matches, + unsigned int num_matches); +{ + return 0; +} #endif /* CONFIG_OF */ #endif /* __LINUX_OF_REG_H */ -- cgit v1.2.3 From 63c7c9e16c8e92cc069854f2babdf82d2d38e4c7 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Thu, 3 Apr 2014 15:32:17 +0100 Subject: regulator: core: Get and put regulator of_node Currently the regulator core does not take an additional reference to the of_node it is passed. This means that the caller must ensure that the of_node is valid for the duration of the regulator's existance. It is reasonable for the framework to assume it is passed a valid of_node but seems onerous for it to assume the caller will keep the node valid for the life-time of the regulator, especially when devm_regulator_register is used and there will likely be no code in the driver called at the point it would be safe to put the of_node. This patch adds an additional of_node_get when the regulator is registered and an of_node_put when it is unregistered in the core. This means individual drivers are free to put their of_node references at the end of probe letting the regulator core handling it from there. This simplifies code on the driver side. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/core.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 9a09f3cdbabb..b97ffd2365d3 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3447,7 +3447,7 @@ regulator_register(const struct regulator_desc *regulator_desc, /* register with sysfs */ rdev->dev.class = ®ulator_class; - rdev->dev.of_node = config->of_node; + rdev->dev.of_node = of_node_get(config->of_node); rdev->dev.parent = dev; dev_set_name(&rdev->dev, "regulator.%d", atomic_inc_return(®ulator_no) - 1); @@ -3589,6 +3589,7 @@ void regulator_unregister(struct regulator_dev *rdev) list_del(&rdev->list); kfree(rdev->constraints); regulator_ena_gpio_free(rdev); + of_node_put(rdev->dev.of_node); device_unregister(&rdev->dev); mutex_unlock(®ulator_list_mutex); } -- cgit v1.2.3 From e80fb721cac4202253939451678c873a222be2fa Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 7 Apr 2014 14:15:24 +0200 Subject: regulator: s5m8767: Remove regulator_dev pointer from state container Don't store pointer to regulator_dev returned by devm_regulator_register() in state container. It isn't used anywhere outside of probe. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s5m8767.c | 17 ++++++----------- 1 file changed, 6 insertions(+), 11 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/s5m8767.c b/drivers/regulator/s5m8767.c index 92f19a005dc3..5daa06626f16 100644 --- a/drivers/regulator/s5m8767.c +++ b/drivers/regulator/s5m8767.c @@ -28,7 +28,6 @@ struct s5m8767_info { struct device *dev; struct sec_pmic_dev *iodev; int num_regulators; - struct regulator_dev **rdev; struct sec_opmode_data *opmode; int ramp_delay; @@ -695,7 +694,6 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); struct sec_platform_data *pdata = iodev->pdata; struct regulator_config config = { }; - struct regulator_dev **rdev; struct s5m8767_info *s5m8767; int i, ret, size, buck_init; @@ -737,11 +735,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) return -ENOMEM; size = sizeof(struct regulator_dev *) * (S5M8767_REG_MAX - 2); - s5m8767->rdev = devm_kzalloc(&pdev->dev, size, GFP_KERNEL); - if (!s5m8767->rdev) - return -ENOMEM; - rdev = s5m8767->rdev; s5m8767->dev = &pdev->dev; s5m8767->iodev = iodev; s5m8767->num_regulators = pdata->num_regulators; @@ -938,6 +932,7 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) const struct sec_voltage_desc *desc; int id = pdata->regulators[i].id; int enable_reg, enable_val; + struct regulator_dev *rdev; desc = reg_voltage_map[id]; if (desc) { @@ -969,21 +964,21 @@ static int s5m8767_pmic_probe(struct platform_device *pdev) s5m8767_regulator_config_ext_control(s5m8767, &pdata->regulators[i], &config); - rdev[i] = devm_regulator_register(&pdev->dev, ®ulators[id], + rdev = devm_regulator_register(&pdev->dev, ®ulators[id], &config); - if (IS_ERR(rdev[i])) { - ret = PTR_ERR(rdev[i]); + if (IS_ERR(rdev)) { + ret = PTR_ERR(rdev); dev_err(s5m8767->dev, "regulator init failed for %d\n", id); return ret; } if (pdata->regulators[i].ext_control_gpio) { - ret = s5m8767_enable_ext_control(s5m8767, rdev[i]); + ret = s5m8767_enable_ext_control(s5m8767, rdev); if (ret < 0) { dev_err(s5m8767->dev, "failed to enable gpio control over %s: %d\n", - rdev[i]->desc->name, ret); + rdev->desc->name, ret); return ret; } } -- cgit v1.2.3 From 011703835f83626048ab75d4ada9ab8ed269b193 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 10:09:06 +0200 Subject: regulator: s2mps11: Move DTS parsing code to separate function Refactor code for parsing DTS to increase a little code readability. The behaviour should not change. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 35 +++++++++++++++++++++++------------ 1 file changed, 23 insertions(+), 12 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index e713c162fbd4..3aba0331fb5d 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -565,12 +565,28 @@ static const struct regulator_desc s2mps14_regulators[] = { regulator_desc_s2mps14_buck1235(5), }; +static int s2mps11_pmic_dt_parse(struct platform_device *pdev, + struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) +{ + struct device_node *reg_np; + + reg_np = of_get_child_by_name(pdev->dev.parent->of_node, "regulators"); + if (!reg_np) { + dev_err(&pdev->dev, "could not find regulators sub-node\n"); + return -EINVAL; + } + + of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); + of_node_put(reg_np); + + return 0; +} + static int s2mps11_pmic_probe(struct platform_device *pdev) { struct sec_pmic_dev *iodev = dev_get_drvdata(pdev->dev.parent); - struct sec_platform_data *pdata = iodev->pdata; + struct sec_platform_data *pdata = NULL; struct of_regulator_match *rdata = NULL; - struct device_node *reg_np = NULL; struct regulator_config config = { }; struct s2mps11_info *s2mps11; int i, ret = 0; @@ -598,7 +614,8 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) }; if (!iodev->dev->of_node) { - if (pdata) { + if (iodev->pdata) { + pdata = iodev->pdata; goto common_reg; } else { dev_err(pdev->dev.parent, @@ -614,15 +631,9 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) for (i = 0; i < s2mps11->rdev_num; i++) rdata[i].name = regulators[i].name; - reg_np = of_get_child_by_name(iodev->dev->of_node, "regulators"); - if (!reg_np) { - dev_err(&pdev->dev, "could not find regulators sub-node\n"); - ret = -EINVAL; + ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11); + if (ret) goto out; - } - - of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); - of_node_put(reg_np); common_reg: platform_set_drvdata(pdev, s2mps11); @@ -633,7 +644,7 @@ common_reg: for (i = 0; i < s2mps11->rdev_num; i++) { struct regulator_dev *regulator; - if (!reg_np) { + if (pdata) { config.init_data = pdata->regulators[i].initdata; config.of_node = pdata->regulators[i].reg_node; } else { -- cgit v1.2.3 From 97f53d710b9f63cbef1c86ee39d9ecfdda6e674c Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 10:09:07 +0200 Subject: regulator: s2mps11: Add external GPIO control for S2MPS14 Add support for external control over GPIO for LDO10, LDO11 and LDO12 S2MPS14 regulators. External control can be turned on by writing 0x0 to control register which in case of other regulators is used for disabling them. These LDO10-LDO12 regulators can be disabled only by I2C GPIO or PWREN pin so the patch actually allows proper way of disabling them. Additionally the GPIO control has two benefits: - It is faster than toggling it over I2C bus. - It allows disabling the regulator during suspend to RAM; The AP will enable it during resume. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- drivers/regulator/s2mps11.c | 67 +++++++++++++++++++++++++++++++++++-- include/linux/mfd/samsung/s2mps14.h | 2 ++ 2 files changed, 67 insertions(+), 2 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/s2mps11.c b/drivers/regulator/s2mps11.c index 3aba0331fb5d..6dad0aa74a47 100644 --- a/drivers/regulator/s2mps11.c +++ b/drivers/regulator/s2mps11.c @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -44,6 +45,8 @@ struct s2mps11_info { * was enabled. */ unsigned int s2mps14_suspend_state:30; + /* Array of size rdev_num with GPIO-s for external sleep control */ + int *ext_control_gpio; }; static int get_ramp_delay(int ramp_delay) @@ -409,6 +412,8 @@ static int s2mps14_regulator_enable(struct regulator_dev *rdev) if (s2mps11->s2mps14_suspend_state & (1 << rdev_get_id(rdev))) val = S2MPS14_ENABLE_SUSPEND; + else if (s2mps11->ext_control_gpio[rdev_get_id(rdev)]) + val = S2MPS14_ENABLE_EXT_CONTROL; else val = rdev->desc->enable_mask; @@ -565,8 +570,40 @@ static const struct regulator_desc s2mps14_regulators[] = { regulator_desc_s2mps14_buck1235(5), }; -static int s2mps11_pmic_dt_parse(struct platform_device *pdev, +static int s2mps14_pmic_enable_ext_control(struct s2mps11_info *s2mps11, + struct regulator_dev *rdev) +{ + return regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + rdev->desc->enable_mask, S2MPS14_ENABLE_EXT_CONTROL); +} + +static void s2mps14_pmic_dt_parse_ext_control_gpio(struct platform_device *pdev, struct of_regulator_match *rdata, struct s2mps11_info *s2mps11) +{ + int *gpio = s2mps11->ext_control_gpio; + unsigned int i; + unsigned int valid_regulators[3] = { S2MPS14_LDO10, S2MPS14_LDO11, + S2MPS14_LDO12 }; + + for (i = 0; i < ARRAY_SIZE(valid_regulators); i++) { + unsigned int reg = valid_regulators[i]; + + if (!rdata[reg].init_data || !rdata[reg].of_node) + continue; + + gpio[reg] = of_get_named_gpio(rdata[reg].of_node, + "samsung,ext-control-gpios", 0); + if (!gpio_is_valid(gpio[reg])) + gpio[reg] = 0; + else + dev_dbg(&pdev->dev, "Using GPIO %d for ext-control over %d/%s\n", + gpio[reg], reg, rdata[reg].name); + } +} + +static int s2mps11_pmic_dt_parse(struct platform_device *pdev, + struct of_regulator_match *rdata, struct s2mps11_info *s2mps11, + enum sec_device_type dev_type) { struct device_node *reg_np; @@ -577,6 +614,9 @@ static int s2mps11_pmic_dt_parse(struct platform_device *pdev, } of_regulator_match(&pdev->dev, reg_np, rdata, s2mps11->rdev_num); + if (dev_type == S2MPS14X) + s2mps14_pmic_dt_parse_ext_control_gpio(pdev, rdata, s2mps11); + of_node_put(reg_np); return 0; @@ -613,6 +653,12 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) return -EINVAL; }; + s2mps11->ext_control_gpio = devm_kzalloc(&pdev->dev, + sizeof(*s2mps11->ext_control_gpio) * s2mps11->rdev_num, + GFP_KERNEL); + if (!s2mps11->ext_control_gpio) + return -ENOMEM; + if (!iodev->dev->of_node) { if (iodev->pdata) { pdata = iodev->pdata; @@ -631,7 +677,7 @@ static int s2mps11_pmic_probe(struct platform_device *pdev) for (i = 0; i < s2mps11->rdev_num; i++) rdata[i].name = regulators[i].name; - ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11); + ret = s2mps11_pmic_dt_parse(pdev, rdata, s2mps11, dev_type); if (ret) goto out; @@ -652,6 +698,12 @@ common_reg: config.of_node = rdata[i].of_node; } + if (s2mps11->ext_control_gpio[i]) { + config.ena_gpio = s2mps11->ext_control_gpio[i]; + config.ena_gpio_flags = GPIOF_OUT_INIT_HIGH; + } else + config.ena_gpio = config.ena_gpio_flags = 0; + regulator = devm_regulator_register(&pdev->dev, ®ulators[i], &config); if (IS_ERR(regulator)) { @@ -660,6 +712,17 @@ common_reg: i); goto out; } + + if (s2mps11->ext_control_gpio[i]) { + ret = s2mps14_pmic_enable_ext_control(s2mps11, + regulator); + if (ret < 0) { + dev_err(&pdev->dev, + "failed to enable GPIO control over %s: %d\n", + regulator->desc->name, ret); + goto out; + } + } } out: diff --git a/include/linux/mfd/samsung/s2mps14.h b/include/linux/mfd/samsung/s2mps14.h index 4b449b8ac548..900cd7a04314 100644 --- a/include/linux/mfd/samsung/s2mps14.h +++ b/include/linux/mfd/samsung/s2mps14.h @@ -148,6 +148,8 @@ enum s2mps14_regulators { #define S2MPS14_ENABLE_SHIFT 6 /* On/Off controlled by PWREN */ #define S2MPS14_ENABLE_SUSPEND (0x01 << S2MPS14_ENABLE_SHIFT) +/* On/Off controlled by LDO10EN or EMMCEN */ +#define S2MPS14_ENABLE_EXT_CONTROL (0x00 << S2MPS14_ENABLE_SHIFT) #define S2MPS14_LDO_N_VOLTAGES (S2MPS14_LDO_VSEL_MASK + 1) #define S2MPS14_BUCK_N_VOLTAGES (S2MPS14_BUCK_VSEL_MASK + 1) -- cgit v1.2.3 From 75dbf0a0f96b0fda180676af51375f5d008b6c9c Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 15 Apr 2014 12:02:08 +0800 Subject: regulator: pbias: Convert to use regulator_[enable|is_enabled]_regmap Since commit ca5d1b3524b4d "regulator: helpers: Modify helpers enabling multi-bit control", we can set enable_val setting for device that use multiple bits for control when using regmap enable/disable/bypass ops. Signed-off-by: Axel Lin Tested-by: Balaji T K Signed-off-by: Mark Brown --- drivers/regulator/pbias-regulator.c | 25 +++---------------------- 1 file changed, 3 insertions(+), 22 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/pbias-regulator.c b/drivers/regulator/pbias-regulator.c index 6d38be3d970c..708ddbb83e29 100644 --- a/drivers/regulator/pbias-regulator.c +++ b/drivers/regulator/pbias-regulator.c @@ -49,33 +49,13 @@ static const unsigned int pbias_volt_table[] = { 3000000 }; -static int pbias_regulator_enable(struct regulator_dev *rdev) -{ - struct pbias_regulator_data *data = rdev_get_drvdata(rdev); - const struct pbias_reg_info *info = data->info; - - return regmap_update_bits(data->syscon, rdev->desc->enable_reg, - info->enable_mask, info->enable); -} - -static int pbias_regulator_is_enable(struct regulator_dev *rdev) -{ - struct pbias_regulator_data *data = rdev_get_drvdata(rdev); - const struct pbias_reg_info *info = data->info; - int value; - - regmap_read(data->syscon, rdev->desc->enable_reg, &value); - - return (value & info->enable_mask) == info->enable; -} - static struct regulator_ops pbias_regulator_voltage_ops = { .list_voltage = regulator_list_voltage_table, .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = regulator_set_voltage_sel_regmap, - .enable = pbias_regulator_enable, + .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, - .is_enabled = pbias_regulator_is_enable, + .is_enabled = regulator_is_enabled_regmap, }; static const struct pbias_reg_info pbias_mmc_omap2430 = { @@ -180,6 +160,7 @@ static int pbias_regulator_probe(struct platform_device *pdev) drvdata[data_idx].desc.vsel_mask = info->vmode; drvdata[data_idx].desc.enable_reg = res->start; drvdata[data_idx].desc.enable_mask = info->enable_mask; + drvdata[data_idx].desc.enable_val = info->enable; cfg.init_data = pbias_matches[idx].init_data; cfg.driver_data = &drvdata[data_idx]; -- cgit v1.2.3 From 073a77d03ee88ae3a5504b3f73632841a55d60a1 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 15 Apr 2014 19:47:38 +0800 Subject: regulator: tps65217: Remove *rdev[] from struct tps65217 Now this driver uses devm_regulator_register() so we don't need to save rdev pointer to tps->rdev[i] for cleanup. Signed-off-by: Axel Lin Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 3 --- include/linux/mfd/tps65217.h | 1 - 2 files changed, 4 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 10b78d2b766a..8482f6ba08a1 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -257,9 +257,6 @@ static int tps65217_regulator_probe(struct platform_device *pdev) pdev->name); return PTR_ERR(rdev); } - - /* Save regulator for cleanup */ - tps->rdev[i] = rdev; } return 0; } diff --git a/include/linux/mfd/tps65217.h b/include/linux/mfd/tps65217.h index 54b5458ec084..95d6938737fd 100644 --- a/include/linux/mfd/tps65217.h +++ b/include/linux/mfd/tps65217.h @@ -254,7 +254,6 @@ struct tps65217 { struct tps65217_board *pdata; unsigned long id; struct regulator_desc desc[TPS65217_NUM_REGULATOR]; - struct regulator_dev *rdev[TPS65217_NUM_REGULATOR]; struct regmap *regmap; }; -- cgit v1.2.3 From 5957ae1fdc50de61d08735d7132ae4f70ae357f7 Mon Sep 17 00:00:00 2001 From: Axel Lin Date: Tue, 15 Apr 2014 19:55:35 +0800 Subject: regulator: tps65217: Use regulator_map_voltage_ascend for LDO1 The voltages in LDO1_VSEL_table are in ascendant order, so use regulator_map_voltage_ascend. Signed-off-by: Axel Lin Signed-off-by: Mark Brown --- drivers/regulator/tps65217-regulator.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/tps65217-regulator.c b/drivers/regulator/tps65217-regulator.c index 8482f6ba08a1..f7ed20a5a8b9 100644 --- a/drivers/regulator/tps65217-regulator.c +++ b/drivers/regulator/tps65217-regulator.c @@ -134,6 +134,7 @@ static struct regulator_ops tps65217_pmic_ldo1_ops = { .get_voltage_sel = regulator_get_voltage_sel_regmap, .set_voltage_sel = tps65217_pmic_set_voltage_sel, .list_voltage = regulator_list_voltage_table, + .map_voltage = regulator_map_voltage_ascend, }; static const struct regulator_desc regulators[] = { -- cgit v1.2.3 From 13b3fde808ed287ad23c4549733fb3e3be785114 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Tue, 15 Apr 2014 13:34:36 +0100 Subject: regulator: core: Use devres for releasing of_regulator_match of_nodes Rather than requiring individual drivers to put the of_nodes returned from of_regulator_match use devres to put them. This also has the benefit it makes the life-time of the of_nodes match the lifetime of the init data also contained in the of_regulator_match structure, which seems more consistent. Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/of_regulator.c | 47 +++++++++++++++++++--------------- include/linux/regulator/of_regulator.h | 7 ----- 2 files changed, 26 insertions(+), 28 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 188e0cb10d03..4672cd2f4632 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -106,6 +106,20 @@ struct regulator_init_data *of_get_regulator_init_data(struct device *dev, } EXPORT_SYMBOL_GPL(of_get_regulator_init_data); +struct devm_of_regulator_matches { + struct of_regulator_match *matches; + unsigned int num_matches; +}; + +static void devm_of_regulator_put_matches(struct device *dev, void *res) +{ + struct devm_of_regulator_matches *devm_matches = res; + int i; + + for (i = 0; i < devm_matches->num_matches; i++) + of_node_put(devm_matches->matches[i].of_node); +} + /** * of_regulator_match - extract multiple regulator init data from device tree. * @dev: device requesting the data @@ -132,10 +146,22 @@ int of_regulator_match(struct device *dev, struct device_node *node, unsigned int i; const char *name; struct device_node *child; + struct devm_of_regulator_matches *devm_matches; if (!dev || !node) return -EINVAL; + devm_matches = devres_alloc(devm_of_regulator_put_matches, + sizeof(struct devm_of_regulator_matches), + GFP_KERNEL); + if (!devm_matches) + return -ENOMEM; + + devm_matches->matches = matches; + devm_matches->num_matches = num_matches; + + devres_add(dev, devm_matches); + for (i = 0; i < num_matches; i++) { struct of_regulator_match *match = &matches[i]; match->init_data = NULL; @@ -172,24 +198,3 @@ int of_regulator_match(struct device *dev, struct device_node *node, return count; } EXPORT_SYMBOL_GPL(of_regulator_match); - -/** - * of_regulator_put_match - put the of_node references from an - * of_regulator_match structure - * @matches: match table for the regulators - * @num_matches: number of entries in match table - * - * This function goes through a match table and calls of_node_put on each - * of_node. - */ -int of_regulator_put_match(struct of_regulator_match *matches, - unsigned int num_matches) -{ - int i; - - for (i = 0; i < num_matches; i++) - of_node_put(matches[i].of_node); - - return 0; -} -EXPORT_SYMBOL_GPL(of_regulator_put_match); diff --git a/include/linux/regulator/of_regulator.h b/include/linux/regulator/of_regulator.h index a5abd8334003..f9217965aaa3 100644 --- a/include/linux/regulator/of_regulator.h +++ b/include/linux/regulator/of_regulator.h @@ -20,8 +20,6 @@ extern struct regulator_init_data extern int of_regulator_match(struct device *dev, struct device_node *node, struct of_regulator_match *matches, unsigned int num_matches); -extern int of_regulator_put_match(struct of_regulator_match *matches, - unsigned int num_matches); #else static inline struct regulator_init_data *of_get_regulator_init_data(struct device *dev, @@ -37,11 +35,6 @@ static inline int of_regulator_match(struct device *dev, { return 0; } -static inline int of_regulator_put_match(struct of_regulator_match *matches, - unsigned int num_matches) -{ - return 0; -} #endif /* CONFIG_OF */ #endif /* __LINUX_OF_REG_H */ -- cgit v1.2.3 From 36bcdf1bb6a16d58f8d60549502fd1da6b27a81a Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Apr 2014 10:01:41 +0100 Subject: regulator: arizona-micsupp: Add processing of init_data from device tree Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-micsupp.c | 37 +++++++++++++++++++++++++++++++++++++ 1 file changed, 37 insertions(+) (limited to 'drivers/regulator') diff --git a/drivers/regulator/arizona-micsupp.c b/drivers/regulator/arizona-micsupp.c index 6fdd9bf6927f..b80ebbe88bac 100644 --- a/drivers/regulator/arizona-micsupp.c +++ b/drivers/regulator/arizona-micsupp.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include #include @@ -195,6 +196,32 @@ static const struct regulator_init_data arizona_micsupp_ext_default = { .num_consumer_supplies = 1, }; +static int arizona_micsupp_of_get_pdata(struct arizona *arizona, + struct regulator_config *config) +{ + struct arizona_pdata *pdata = &arizona->pdata; + struct arizona_micsupp *micsupp = config->driver_data; + struct device_node *np; + struct regulator_init_data *init_data; + + np = of_get_child_by_name(arizona->dev->of_node, "micvdd"); + + if (np) { + config->of_node = np; + + init_data = of_get_regulator_init_data(arizona->dev, np); + + if (init_data) { + init_data->consumer_supplies = &micsupp->supply; + init_data->num_consumer_supplies = 1; + + pdata->micvdd = init_data; + } + } + + return 0; +} + static int arizona_micsupp_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -234,6 +261,14 @@ static int arizona_micsupp_probe(struct platform_device *pdev) config.driver_data = micsupp; config.regmap = arizona->regmap; + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_micsupp_of_get_pdata(arizona, &config); + if (ret < 0) + return ret; + } + } + if (arizona->pdata.micvdd) config.init_data = arizona->pdata.micvdd; else @@ -253,6 +288,8 @@ static int arizona_micsupp_probe(struct platform_device *pdev) return ret; } + of_node_put(config.of_node); + platform_set_drvdata(pdev, micsupp); return 0; -- cgit v1.2.3 From 4a8c475f5fd5c1271dba36a453d666d5ed473aa6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Apr 2014 10:01:38 +0100 Subject: regulator: arizona-ldo1: Move setup processing from arizona-core It is more idiomatic to process things relating to the regulator in its driver. This patch moves both processing of device tree relating to the regulator and checking if the regulator is external from arizona-core into the regulator driver. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 12 +++--------- drivers/regulator/arizona-ldo1.c | 27 +++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 9 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 37b5e1447d02..07e6e27be23c 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -533,7 +533,6 @@ static int arizona_of_get_core_pdata(struct arizona *arizona) int ret, i; pdata->reset = arizona_of_get_named_gpio(arizona, "wlf,reset", true); - pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true); ret = of_property_read_u32_array(arizona->dev->of_node, "wlf,gpio-defaults", @@ -665,6 +664,9 @@ int arizona_dev_init(struct arizona *arizona) return -EINVAL; } + /* Mark DCVDD as external, LDO1 driver will clear if internal */ + arizona->external_dcvdd = true; + ret = mfd_add_devices(arizona->dev, -1, early_devs, ARRAY_SIZE(early_devs), NULL, 0, NULL); if (ret != 0) { @@ -864,14 +866,6 @@ int arizona_dev_init(struct arizona *arizona) arizona->pdata.gpio_defaults[i]); } - /* - * LDO1 can only be used to supply DCVDD so if it has no - * consumers then DCVDD is supplied externally. - */ - if (arizona->pdata.ldo1 && - arizona->pdata.ldo1->num_consumer_supplies == 0) - arizona->external_dcvdd = true; - pm_runtime_set_autosuspend_delay(arizona->dev, 100); pm_runtime_use_autosuspend(arizona->dev); pm_runtime_enable(arizona->dev); diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index b1033d30b504..2248733ea394 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -178,6 +178,15 @@ static const struct regulator_init_data arizona_ldo1_default = { .num_consumer_supplies = 1, }; +static int arizona_ldo1_of_get_pdata(struct arizona *arizona) +{ + struct arizona_pdata *pdata = &arizona->pdata; + + pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true); + + return 0; +} + static int arizona_ldo1_probe(struct platform_device *pdev) { struct arizona *arizona = dev_get_drvdata(pdev->dev.parent); @@ -186,6 +195,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) struct arizona_ldo1 *ldo1; int ret; + arizona->external_dcvdd = false; + ldo1 = devm_kzalloc(&pdev->dev, sizeof(*ldo1), GFP_KERNEL); if (!ldo1) return -ENOMEM; @@ -216,6 +227,15 @@ static int arizona_ldo1_probe(struct platform_device *pdev) config.dev = arizona->dev; config.driver_data = ldo1; config.regmap = arizona->regmap; + + if (IS_ENABLED(CONFIG_OF)) { + if (!dev_get_platdata(arizona->dev)) { + ret = arizona_ldo1_of_get_pdata(arizona); + if (ret < 0) + return ret; + } + } + config.ena_gpio = arizona->pdata.ldoena; if (arizona->pdata.ldo1) @@ -223,6 +243,13 @@ static int arizona_ldo1_probe(struct platform_device *pdev) else config.init_data = &ldo1->init_data; + /* + * LDO1 can only be used to supply DCVDD so if it has no + * consumers then DCVDD is supplied externally. + */ + if (config.init_data->num_consumer_supplies == 0) + arizona->external_dcvdd = true; + ldo1->regulator = devm_regulator_register(&pdev->dev, desc, &config); if (IS_ERR(ldo1->regulator)) { ret = PTR_ERR(ldo1->regulator); -- cgit v1.2.3 From 2cce4be9e6b885c595816c45a80bcce95dae6d30 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Apr 2014 10:01:39 +0100 Subject: regulator: arizona-ldo1: Add processing of init_data from device tree Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- drivers/regulator/arizona-ldo1.c | 34 ++++++++++++++++++++++++++++++++-- 1 file changed, 32 insertions(+), 2 deletions(-) (limited to 'drivers/regulator') diff --git a/drivers/regulator/arizona-ldo1.c b/drivers/regulator/arizona-ldo1.c index 2248733ea394..d3787e11f535 100644 --- a/drivers/regulator/arizona-ldo1.c +++ b/drivers/regulator/arizona-ldo1.c @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -178,12 +179,39 @@ static const struct regulator_init_data arizona_ldo1_default = { .num_consumer_supplies = 1, }; -static int arizona_ldo1_of_get_pdata(struct arizona *arizona) +static int arizona_ldo1_of_get_pdata(struct arizona *arizona, + struct regulator_config *config) { struct arizona_pdata *pdata = &arizona->pdata; + struct arizona_ldo1 *ldo1 = config->driver_data; + struct device_node *init_node, *dcvdd_node; + struct regulator_init_data *init_data; pdata->ldoena = arizona_of_get_named_gpio(arizona, "wlf,ldoena", true); + init_node = of_get_child_by_name(arizona->dev->of_node, "ldo1"); + dcvdd_node = of_parse_phandle(arizona->dev->of_node, "DCVDD-supply", 0); + + if (init_node) { + config->of_node = init_node; + + init_data = of_get_regulator_init_data(arizona->dev, init_node); + + if (init_data) { + init_data->consumer_supplies = &ldo1->supply; + init_data->num_consumer_supplies = 1; + + if (dcvdd_node && dcvdd_node != init_node) + arizona->external_dcvdd = true; + + pdata->ldo1 = init_data; + } + } else if (dcvdd_node) { + arizona->external_dcvdd = true; + } + + of_node_put(dcvdd_node); + return 0; } @@ -230,7 +258,7 @@ static int arizona_ldo1_probe(struct platform_device *pdev) if (IS_ENABLED(CONFIG_OF)) { if (!dev_get_platdata(arizona->dev)) { - ret = arizona_ldo1_of_get_pdata(arizona); + ret = arizona_ldo1_of_get_pdata(arizona, &config); if (ret < 0) return ret; } @@ -258,6 +286,8 @@ static int arizona_ldo1_probe(struct platform_device *pdev) return ret; } + of_node_put(config.of_node); + platform_set_drvdata(pdev, ldo1); return 0; -- cgit v1.2.3