From d7e9e2622d7054183fc0be28a6e48084004fc3eb Mon Sep 17 00:00:00 2001 From: Finley Xiao Date: Wed, 18 Apr 2018 10:01:01 +0800 Subject: [PATCH] regulator: core: Add support to limit min_uV during system startup Now a regulator device can supply multiple consumers at the same time, if a consumer starts and set a low voltage, another consumer doesn't start in kernel but has been set a high frequency in bootloader will abort. This patch Adds support to limit min_uV during kernel startup to make sure the voltage can suit the needs of all consumers. Change-Id: Ibd16a8e44916798021e2470c90a8e3488df206f4 Signed-off-by: Finley Xiao --- .../bindings/regulator/regulator.txt | 2 + drivers/regulator/core.c | 65 +++++++++++++++++++ drivers/regulator/internal.h | 1 + drivers/regulator/of_regulator.c | 3 + include/linux/regulator/machine.h | 6 ++ 5 files changed, 77 insertions(+) diff --git a/Documentation/devicetree/bindings/regulator/regulator.txt b/Documentation/devicetree/bindings/regulator/regulator.txt index a7cd36877bfe..542f40bc3cb0 100644 --- a/Documentation/devicetree/bindings/regulator/regulator.txt +++ b/Documentation/devicetree/bindings/regulator/regulator.txt @@ -5,6 +5,8 @@ Optional properties: - regulator-min-microvolt: smallest voltage consumers may set - regulator-max-microvolt: largest voltage consumers may set - regulator-microvolt-offset: Offset applied to voltages to compensate for voltage drops +- regulator-early-min-microvolt: minimum voltage during system startup, make sure we + select a voltage that suits the needs of all regulator consumers - regulator-min-microamp: smallest current consumers may set - regulator-max-microamp: largest current consumers may set - regulator-input-current-limit-microamp: maximum input current regulator allows diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index ca74982a4fd5..d6d5527f660c 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -56,6 +56,7 @@ static DEFINE_MUTEX(regulator_list_mutex); static LIST_HEAD(regulator_map_list); static LIST_HEAD(regulator_ena_gpio_list); static LIST_HEAD(regulator_supply_alias_list); +static LIST_HEAD(regulator_early_min_volt_list); static bool has_full_constraints; static struct dentry *debugfs_root; @@ -4430,6 +4431,27 @@ static inline void rdev_init_debugfs(struct regulator_dev *rdev) #endif +static void rdev_init_early_min_volt(struct regulator_dev *rdev) +{ + struct regulator *regulator; + + if (!rdev->constraints->early_min_uV) + return; + + regulator = regulator_get(NULL, rdev_get_name(rdev)); + if (IS_ERR(regulator)) { + rdev_err(rdev, "regulator get failed, ret=%ld\n", + PTR_ERR(regulator)); + return; + } + + regulator->voltage[PM_SUSPEND_ON].min_uV = + rdev->constraints->early_min_uV; + regulator->voltage[PM_SUSPEND_ON].max_uV = rdev->constraints->max_uV; + + list_add(®ulator->early_min_list, ®ulator_early_min_volt_list); +} + static int regulator_register_resolve_supply(struct device *dev, void *data) { struct regulator_dev *rdev = dev_to_rdev(dev); @@ -4700,6 +4722,7 @@ regulator_register(const struct regulator_desc *regulator_desc, } rdev_init_debugfs(rdev); + rdev_init_early_min_volt(rdev); /* try to resolve regulators supply since a new one was registered */ class_for_each_device(®ulator_class, NULL, NULL, @@ -5135,6 +5158,46 @@ unlock: return 0; } +static void __init regulator_release_early_min_volt(void) +{ + struct regulator *regulator, *n, *reg; + struct regulator_dev *rdev; + int min_uV = 0, max_uV = 0, ret = 0; + + if (list_empty(®ulator_early_min_volt_list)) + return; + + list_for_each_entry_safe(regulator, n, ®ulator_early_min_volt_list, + early_min_list) { + rdev = regulator->rdev; + + regulator_lock_supply(rdev); + + regulator->voltage[PM_SUSPEND_ON].min_uV = 0; + regulator->voltage[PM_SUSPEND_ON].max_uV = 0; + min_uV = rdev->constraints->min_uV; + max_uV = rdev->constraints->max_uV; + + list_for_each_entry(reg, &rdev->consumer_list, list) { + if (!reg->voltage[PM_SUSPEND_ON].min_uV && + !reg->voltage[PM_SUSPEND_ON].max_uV) + continue; + ret = regulator_set_voltage_unlocked(regulator, min_uV, + max_uV, + PM_SUSPEND_ON); + if (ret) + rdev_err(rdev, "set voltage(%d, %d) failed\n", + min_uV, max_uV); + break; + } + + regulator_unlock_supply(rdev); + + list_del(®ulator->early_min_list); + regulator_put(regulator); + } +} + static int __init regulator_init_complete(void) { /* @@ -5167,6 +5230,8 @@ static int __init regulator_init_complete(void) class_for_each_device(®ulator_class, NULL, NULL, regulator_register_fill_coupling_array); + regulator_release_early_min_volt(); + return 0; } late_initcall_sync(regulator_init_complete); diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h index 943926a156f2..3c133d2f2dbd 100644 --- a/drivers/regulator/internal.h +++ b/drivers/regulator/internal.h @@ -39,6 +39,7 @@ struct regulator_voltage { struct regulator { struct device *dev; struct list_head list; + struct list_head early_min_list; unsigned int always_on:1; unsigned int bypass:1; int uA_load; diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c index 210fc20f7de7..0ddb9432d3f0 100644 --- a/drivers/regulator/of_regulator.c +++ b/drivers/regulator/of_regulator.c @@ -43,6 +43,9 @@ static void of_get_regulation_constraints(struct device_node *np, if (!of_property_read_u32(np, "regulator-max-microvolt", &pval)) constraints->max_uV = pval; + if (!of_property_read_u32(np, "regulator-early-min-microvolt", &pval)) + constraints->early_min_uV = pval; + /* Voltage change possible? */ if (constraints->min_uV != constraints->max_uV) constraints->valid_ops_mask |= REGULATOR_CHANGE_VOLTAGE; diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h index a459a5e973a7..9a355d346dba 100644 --- a/include/linux/regulator/machine.h +++ b/include/linux/regulator/machine.h @@ -98,6 +98,9 @@ struct regulator_state { * @uV_offset: Offset applied to voltages from consumer to compensate for * voltage drops. * + * @early_min_uV: Minimum voltage during system startup, make sure we select + * a voltage that suits the needs of all regulator consumers. + * * @min_uA: Smallest current consumers may set. * @max_uA: Largest current consumers may set. * @ilim_uA: Maximum input current. @@ -146,6 +149,9 @@ struct regulation_constraints { int min_uV; int max_uV; + /* Minimum voltage during system startup */ + int early_min_uV; + int uV_offset; /* current output range (inclusive) - for current control */