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 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(-) 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(+) 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(-) 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 5efe144681c183e1ca7b0053ad1113667aa7fde2 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Fri, 4 Apr 2014 09:29:58 +0100 Subject: regulator: core: Fix typo in of_regulator.h Fix a typo from my patch adding of_regulator_put_match in the patch: regulator: core: Add helper to put of_nodes from matches Signed-off-by: Charles Keepax Signed-off-by: Mark Brown --- include/linux/regulator/of_regulator.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/include/linux/regulator/of_regulator.h b/include/linux/regulator/of_regulator.h index 06528516aa15..a5abd8334003 100644 --- a/include/linux/regulator/of_regulator.h +++ b/include/linux/regulator/of_regulator.h @@ -38,7 +38,7 @@ 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); + unsigned int num_matches) { return 0; } -- 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(-) 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(-) 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(-) 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 9b63cfbfd13b609e8d496852b6714ac686e31901 Mon Sep 17 00:00:00 2001 From: Krzysztof Kozlowski Date: Mon, 14 Apr 2014 10:09:08 +0200 Subject: regulator: s2mps11: Document external GPIO control Add documentation for new property for controlling (enable/disable) some of the S2MPS14 regulators by GPIO. Signed-off-by: Krzysztof Kozlowski Signed-off-by: Mark Brown --- Documentation/devicetree/bindings/mfd/s2mps11.txt | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/Documentation/devicetree/bindings/mfd/s2mps11.txt b/Documentation/devicetree/bindings/mfd/s2mps11.txt index 802e839b0829..d81ba30c0d8b 100644 --- a/Documentation/devicetree/bindings/mfd/s2mps11.txt +++ b/Documentation/devicetree/bindings/mfd/s2mps11.txt @@ -56,6 +56,20 @@ for a particular group of BUCKs. So provide same regulator-ramp-delay. Grouping of BUCKs sharing ramp rate setting is as follow : BUCK[1, 6], BUCK[3, 4], and BUCK[7, 8, 10] +On S2MPS14 the LDO10, LDO11 and LDO12 can be configured to external control +over GPIO. To turn this feature on this property must be added to the regulator +sub-node: + - samsung,ext-control-gpios: GPIO specifier for one GPIO + controlling this regulator (enable/disable); +Example: + LDO12 { + regulator-name = "V_EMMC_2.8V"; + regulator-min-microvolt = <2800000>; + regulator-max-microvolt = <2800000>; + samsung,ext-control-gpios = <&gpk0 2 0>; + }; + + The regulator constraints inside the regulator nodes use the standard regulator bindings which are documented elsewhere. -- 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(-) 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(-) 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 290414499cf94284a97cc3c33214d13ccfcd896a Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 16 Apr 2014 16:12:28 -0700 Subject: regulator: tps65090: Allow setting the overcurrent wait time The tps65090 regulator allows you to specify how long you want it to wait before detecting an overcurrent condition. Allow specifying that through the device tree (or through platform data). Signed-off-by: Doug Anderson Signed-off-by: Simon Glass Signed-off-by: Michael Spang Signed-off-by: Sean Paul Signed-off-by: Mark Brown --- .../devicetree/bindings/regulator/tps65090.txt | 4 ++ drivers/regulator/tps65090-regulator.c | 56 ++++++++++++++++++++++ include/linux/mfd/tps65090.h | 5 ++ 3 files changed, 65 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/tps65090.txt b/Documentation/devicetree/bindings/regulator/tps65090.txt index 313a60ba61d8..340980239ea9 100644 --- a/Documentation/devicetree/bindings/regulator/tps65090.txt +++ b/Documentation/devicetree/bindings/regulator/tps65090.txt @@ -21,6 +21,10 @@ Optional properties: number should be provided. If it is externally controlled and no GPIO entry then driver will just configure this rails as external control and will not provide any enable/disable APIs. +- ti,overcurrent-wait: This is applicable to FET registers, which have a + poorly defined "overcurrent wait" field. If this property is present it + should be between 0 - 3. If this property isn't present we won't touch the + "overcurrent wait" field and we'll leave it to the BIOS/EC to deal with. Each regulator is defined using the standard binding for regulators. diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index 2e92ef68574d..ca04e9f010e1 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -28,15 +28,58 @@ #include #include +#define CTRL_WT_BIT 2 /* Regulator wait time 0 bit */ + +#define MAX_OVERCURRENT_WAIT 3 /* Overcurrent wait must be <= this */ + +/** + * struct tps65090_regulator - Per-regulator data for a tps65090 regulator + * + * @dev: Pointer to our device. + * @desc: The struct regulator_desc for the regulator. + * @rdev: The struct regulator_dev for the regulator. + * @overcurrent_wait_valid: True if overcurrent_wait is valid. + * @overcurrent_wait: For FETs, the value to put in the WTFET bitfield. + */ + struct tps65090_regulator { struct device *dev; struct regulator_desc *desc; struct regulator_dev *rdev; + bool overcurrent_wait_valid; + int overcurrent_wait; }; static struct regulator_ops tps65090_ext_control_ops = { }; +/** + * tps65090_reg_set_overcurrent_wait - Setup overcurrent wait + * + * This will set the overcurrent wait time based on what's in the regulator + * info. + * + * @ri: Overall regulator data + * @rdev: Regulator device + * + * Return: 0 if no error, non-zero if there was an error writing the register. + */ +static int tps65090_reg_set_overcurrent_wait(struct tps65090_regulator *ri, + struct regulator_dev *rdev) +{ + int ret; + + ret = regmap_update_bits(rdev->regmap, rdev->desc->enable_reg, + MAX_OVERCURRENT_WAIT << CTRL_WT_BIT, + ri->overcurrent_wait << CTRL_WT_BIT); + if (ret) { + dev_err(&rdev->dev, "Error updating overcurrent wait %#x\n", + rdev->desc->enable_reg); + } + + return ret; +} + static struct regulator_ops tps65090_reg_contol_ops = { .enable = regulator_enable_regmap, .disable = regulator_disable_regmap, @@ -209,6 +252,11 @@ static struct tps65090_platform_data *tps65090_parse_dt_reg_data( rpdata->gpio = of_get_named_gpio(np, "dcdc-ext-control-gpios", 0); + if (of_property_read_u32(tps65090_matches[idx].of_node, + "ti,overcurrent-wait", + &rpdata->overcurrent_wait) == 0) + rpdata->overcurrent_wait_valid = true; + tps65090_pdata->reg_pdata[idx] = rpdata; } return tps65090_pdata; @@ -258,6 +306,8 @@ static int tps65090_regulator_probe(struct platform_device *pdev) ri = &pmic[num]; ri->dev = &pdev->dev; ri->desc = &tps65090_regulator_desc[num]; + ri->overcurrent_wait_valid = tps_pdata->overcurrent_wait_valid; + ri->overcurrent_wait = tps_pdata->overcurrent_wait; /* * TPS5090 DCDC support the control from external digital input. @@ -299,6 +349,12 @@ static int tps65090_regulator_probe(struct platform_device *pdev) } ri->rdev = rdev; + if (ri->overcurrent_wait_valid) { + ret = tps65090_reg_set_overcurrent_wait(ri, rdev); + if (ret < 0) + return ret; + } + /* Enable external control if it is require */ if (tps_pdata && is_dcdc(num) && tps_pdata->reg_init_data && tps_pdata->enable_ext_control) { diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069413e7..f25adfa97c73 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -78,11 +78,16 @@ struct tps65090 { * DCDC1, DCDC2 and DCDC3. * @gpio: Gpio number if external control is enabled and controlled through * gpio. + * @overcurrent_wait_valid: True if the overcurrent_wait should be applied. + * @overcurrent_wait: Value to set as the overcurrent wait time. This is the + * actual bitfield value, not a time in ms (valid value are 0 - 3). */ struct tps65090_regulator_plat_data { struct regulator_init_data *reg_init_data; bool enable_ext_control; int gpio; + bool overcurrent_wait_valid; + int overcurrent_wait; }; struct tps65090_platform_data { -- 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(+) 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(-) 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 e4fcb1d6148284a10c314fce2a488cf19ce886f6 Mon Sep 17 00:00:00 2001 From: Charles Keepax Date: Wed, 16 Apr 2014 10:01:37 +0100 Subject: mfd: arizona: Factor out read of device tree GPIOs This patch factors out the reading of GPIOs for the Arizona devices into a helper function. Signed-off-by: Charles Keepax Acked-by: Lee Jones Signed-off-by: Mark Brown --- drivers/mfd/arizona-core.c | 31 ++++++++++++++++++++++--------- include/linux/mfd/arizona/core.h | 3 +++ 2 files changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/mfd/arizona-core.c b/drivers/mfd/arizona-core.c index 1c3ae57082ed..37b5e1447d02 100644 --- a/drivers/mfd/arizona-core.c +++ b/drivers/mfd/arizona-core.c @@ -508,19 +508,32 @@ int arizona_of_get_type(struct device *dev) } EXPORT_SYMBOL_GPL(arizona_of_get_type); +int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory) +{ + int gpio; + + gpio = of_get_named_gpio(arizona->dev->of_node, prop, 0); + if (gpio < 0) { + if (mandatory) + dev_err(arizona->dev, + "Mandatory DT gpio %s missing/malformed: %d\n", + prop, gpio); + + gpio = 0; + } + + return gpio; +} +EXPORT_SYMBOL_GPL(arizona_of_get_named_gpio); + static int arizona_of_get_core_pdata(struct arizona *arizona) { + struct arizona_pdata *pdata = &arizona->pdata; int ret, i; - arizona->pdata.reset = of_get_named_gpio(arizona->dev->of_node, - "wlf,reset", 0); - if (arizona->pdata.reset < 0) - arizona->pdata.reset = 0; - - arizona->pdata.ldoena = of_get_named_gpio(arizona->dev->of_node, - "wlf,ldoena", 0); - if (arizona->pdata.ldoena < 0) - arizona->pdata.ldoena = 0; + 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", diff --git a/include/linux/mfd/arizona/core.h b/include/linux/mfd/arizona/core.h index 5cf8b91ce996..6d9371f88875 100644 --- a/include/linux/mfd/arizona/core.h +++ b/include/linux/mfd/arizona/core.h @@ -124,4 +124,7 @@ int wm5102_patch(struct arizona *arizona); int wm5110_patch(struct arizona *arizona); int wm8997_patch(struct arizona *arizona); +extern int arizona_of_get_named_gpio(struct arizona *arizona, const char *prop, + bool mandatory); + #endif -- 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(+) 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(-) 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(-) 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 From 7d811771c95ebab358eca8e68b53efe09e3a6a96 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 16 Apr 2014 16:12:25 -0700 Subject: mfd: tps65090: Don't tell child devices we have an IRQ if we don't If we weren't given an interrupt we shouldn't tell child devices (like the tps65090 charger) that they have an interrupt. This is needed so that we can support polling mode in the tps65090 charger driver. See also (charger: tps65090: Allow charger module to be used when no irq). Signed-off-by: Doug Anderson Signed-off-by: Lee Jones --- drivers/mfd/tps65090.c | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index ba1a25d758c1..c3cddb4c3a1a 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -64,11 +64,16 @@ static struct resource charger_resources[] = { } }; -static const struct mfd_cell tps65090s[] = { - { +enum tps65090_cells { + PMIC = 0, + CHARGER = 1, +}; + +static struct mfd_cell tps65090s[] = { + [PMIC] = { .name = "tps65090-pmic", }, - { + [CHARGER] = { .name = "tps65090-charger", .num_resources = ARRAY_SIZE(charger_resources), .resources = &charger_resources[0], @@ -211,6 +216,9 @@ static int tps65090_i2c_probe(struct i2c_client *client, "IRQ init failed with err: %d\n", ret); return ret; } + } else { + /* Don't tell children they have an IRQ that'll never fire */ + tps65090s[CHARGER].num_resources = 0; } ret = mfd_add_devices(tps65090->dev, -1, tps65090s, -- cgit v1.2.3 From c42ba72ec3a7a1b6aa30122931f1f4b91b601c31 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 16 Apr 2014 16:12:27 -0700 Subject: mfd: tps65090: Stop caching most registers Nearly all of the registers in tps65090 combine control bits and status bits. Turn off caching of all registers except the select few that can be cached. In order to avoid adding more duplicate #defines, we also move some register offset definitions to the mfd driver (and resolve inconsistent names). Signed-off-by: Doug Anderson Acked-by: Mark Brown Signed-off-by: Lee Jones --- drivers/mfd/tps65090.c | 27 ++++++++++++++------------- drivers/power/tps65090-charger.c | 11 ----------- include/linux/mfd/tps65090.h | 14 ++++++++++++++ 3 files changed, 28 insertions(+), 24 deletions(-) diff --git a/drivers/mfd/tps65090.c b/drivers/mfd/tps65090.c index c3cddb4c3a1a..1c3e6e2efe41 100644 --- a/drivers/mfd/tps65090.c +++ b/drivers/mfd/tps65090.c @@ -32,14 +32,6 @@ #define NUM_INT_REG 2 #define TOTAL_NUM_REG 0x18 -/* interrupt status registers */ -#define TPS65090_INT_STS 0x0 -#define TPS65090_INT_STS2 0x1 - -/* interrupt mask registers */ -#define TPS65090_INT_MSK 0x2 -#define TPS65090_INT_MSK2 0x3 - #define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1 #define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2 #define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3 @@ -144,17 +136,26 @@ static struct regmap_irq_chip tps65090_irq_chip = { .irqs = tps65090_irqs, .num_irqs = ARRAY_SIZE(tps65090_irqs), .num_regs = NUM_INT_REG, - .status_base = TPS65090_INT_STS, - .mask_base = TPS65090_INT_MSK, + .status_base = TPS65090_REG_INTR_STS, + .mask_base = TPS65090_REG_INTR_MASK, .mask_invert = true, }; static bool is_volatile_reg(struct device *dev, unsigned int reg) { - if ((reg == TPS65090_INT_STS) || (reg == TPS65090_INT_STS2)) - return true; - else + /* Nearly all registers have status bits mixed in, except a few */ + switch (reg) { + case TPS65090_REG_INTR_MASK: + case TPS65090_REG_INTR_MASK2: + case TPS65090_REG_CG_CTRL0: + case TPS65090_REG_CG_CTRL1: + case TPS65090_REG_CG_CTRL2: + case TPS65090_REG_CG_CTRL3: + case TPS65090_REG_CG_CTRL4: + case TPS65090_REG_CG_CTRL5: return false; + } + return true; } static const struct regmap_config tps65090_regmap_config = { diff --git a/drivers/power/tps65090-charger.c b/drivers/power/tps65090-charger.c index 8fc9d6df87f6..1685f63b9e5d 100644 --- a/drivers/power/tps65090-charger.c +++ b/drivers/power/tps65090-charger.c @@ -28,17 +28,6 @@ #include -#define TPS65090_REG_INTR_STS 0x00 -#define TPS65090_REG_INTR_MASK 0x02 -#define TPS65090_REG_CG_CTRL0 0x04 -#define TPS65090_REG_CG_CTRL1 0x05 -#define TPS65090_REG_CG_CTRL2 0x06 -#define TPS65090_REG_CG_CTRL3 0x07 -#define TPS65090_REG_CG_CTRL4 0x08 -#define TPS65090_REG_CG_CTRL5 0x09 -#define TPS65090_REG_CG_STATUS1 0x0a -#define TPS65090_REG_CG_STATUS2 0x0b - #define TPS65090_CHARGER_ENABLE BIT(0) #define TPS65090_VACG BIT(1) #define TPS65090_NOITERM BIT(5) diff --git a/include/linux/mfd/tps65090.h b/include/linux/mfd/tps65090.h index 3f43069413e7..45f0f9d2ed25 100644 --- a/include/linux/mfd/tps65090.h +++ b/include/linux/mfd/tps65090.h @@ -64,6 +64,20 @@ enum { TPS65090_REGULATOR_MAX, }; +/* Register addresses */ +#define TPS65090_REG_INTR_STS 0x00 +#define TPS65090_REG_INTR_STS2 0x01 +#define TPS65090_REG_INTR_MASK 0x02 +#define TPS65090_REG_INTR_MASK2 0x03 +#define TPS65090_REG_CG_CTRL0 0x04 +#define TPS65090_REG_CG_CTRL1 0x05 +#define TPS65090_REG_CG_CTRL2 0x06 +#define TPS65090_REG_CG_CTRL3 0x07 +#define TPS65090_REG_CG_CTRL4 0x08 +#define TPS65090_REG_CG_CTRL5 0x09 +#define TPS65090_REG_CG_STATUS1 0x0a +#define TPS65090_REG_CG_STATUS2 0x0b + struct tps65090 { struct device *dev; struct regmap *rmap; -- cgit v1.2.3 From ed11f1ead54bf0398a4303b51125622113c9c9e1 Mon Sep 17 00:00:00 2001 From: Doug Anderson Date: Wed, 23 Apr 2014 08:56:05 -0700 Subject: regulator: tps65090: Make FETs more reliable by adding retries An issue was discovered with tps65090 where sometimes the FETs wouldn't actually turn on when requested (they would report overcurrent). The most problematic FET was the one used for the LCD backlight on the Samsung ARM Chromebook (FET1). Problems were especially prevalent when the device was plugged in to AC power (when the backlight voltage was higher). Mitigate the problem by adding retries on the enables of the FETs, which works around the problem fairly effectively. Signed-off-by: Doug Anderson Signed-off-by: Simon Glass Signed-off-by: Michael Spang Signed-off-by: Sean Paul Reviewed-by: Simon Glass Signed-off-by: Mark Brown --- drivers/regulator/tps65090-regulator.c | 155 +++++++++++++++++++++++++++++---- 1 file changed, 140 insertions(+), 15 deletions(-) diff --git a/drivers/regulator/tps65090-regulator.c b/drivers/regulator/tps65090-regulator.c index ca04e9f010e1..2057e2e311de 100644 --- a/drivers/regulator/tps65090-regulator.c +++ b/drivers/regulator/tps65090-regulator.c @@ -17,6 +17,7 @@ */ #include +#include #include #include #include @@ -28,7 +29,13 @@ #include #include +#define MAX_CTRL_READ_TRIES 5 +#define MAX_FET_ENABLE_TRIES 1000 + +#define CTRL_EN_BIT 0 /* Regulator enable bit, active high */ #define CTRL_WT_BIT 2 /* Regulator wait time 0 bit */ +#define CTRL_PG_BIT 4 /* Regulator power good bit, 1=good */ +#define CTRL_TO_BIT 7 /* Regulator timeout bit, 1=wait */ #define MAX_OVERCURRENT_WAIT 3 /* Overcurrent wait must be <= this */ @@ -80,40 +87,158 @@ static int tps65090_reg_set_overcurrent_wait(struct tps65090_regulator *ri, return ret; } -static struct regulator_ops tps65090_reg_contol_ops = { +/** + * tps6