@@ -55,6 +55,7 @@ static LIST_HEAD(regulator_map_list);
static LIST_HEAD(regulator_ena_gpio_list);
static LIST_HEAD(regulator_supply_alias_list);
static bool has_full_constraints;
+static bool regulator_has_booted;
static struct dentry *debugfs_root;
@@ -1030,6 +1031,13 @@ static int set_machine_constraints(struct regulator_dev *rdev,
if (!rdev->constraints)
return -ENOMEM;
+ /*
+ * If a regulator driver is registered after late_initcall, the
+ * boot_protection should be ingnored.
+ */
+ if (regulator_has_booted)
+ rdev->constraints->boot_protection = 0;
+
ret = machine_constraints_voltage(rdev, rdev->constraints);
if (ret != 0)
return ret;
@@ -2195,8 +2203,14 @@ static int _regulator_disable(struct regulator_dev *rdev)
if (rdev->use_count == 1 &&
(rdev->constraints && !rdev->constraints->always_on)) {
- /* we are last user */
- if (regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
+ /*
+ * We are last user.
+ *
+ * If boot_protection is set, we only clear use_count,
+ * and regulator_init_complete() will disable it.
+ */
+ if (!rdev->constraints->boot_protection &&
+ regulator_ops_is_valid(rdev, REGULATOR_CHANGE_STATUS)) {
ret = _notifier_call_chain(rdev,
REGULATOR_EVENT_PRE_DISABLE,
NULL);
@@ -2297,6 +2311,10 @@ int regulator_force_disable(struct regulator *regulator)
struct regulator_dev *rdev = regulator->rdev;
int ret;
+ WARN(rdev->constraints->boot_protection,
+ "disable regulator %s with boot protection flag\n",
+ rdev->desc->name);
+
mutex_lock(&rdev->mutex);
regulator->uA_load = 0;
ret = _regulator_force_disable(regulator->rdev);
@@ -2852,6 +2870,10 @@ static int regulator_set_voltage_unlocked(struct regulator *regulator,
if (ret < 0)
goto out2;
+ /* We need to change voltage, but boot_protection is set. */
+ if (rdev->constraints->boot_protection)
+ goto out;
+
if (rdev->supply && (rdev->desc->min_dropout_uV ||
!rdev->desc->ops->get_voltage)) {
int current_supply_uV;
@@ -3069,6 +3091,9 @@ int regulator_sync_voltage(struct regulator *regulator)
if (ret < 0)
goto out;
+ if (rdev->constraints->boot_protection)
+ goto out;
+
ret = _regulator_do_set_voltage(rdev, min_uV, max_uV);
out:
@@ -3161,6 +3186,15 @@ int regulator_set_current_limit(struct regulator *regulator,
if (ret < 0)
goto out;
+ /*
+ * Stage new current value, and applied it later.
+ */
+ if (rdev->constraints->boot_protection) {
+ regulator->min_uA = min_uA;
+ regulator->max_uA = max_uA;
+ goto out;
+ }
+
ret = rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA);
out:
mutex_unlock(&rdev->mutex);
@@ -3240,6 +3274,11 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
if (ret < 0)
goto out;
+ if (rdev->constraints->boot_protection) {
+ rdev->boot_mode = mode;
+ goto out;
+ }
+
ret = rdev->desc->ops->set_mode(rdev, mode);
out:
mutex_unlock(&rdev->mutex);
@@ -3306,11 +3345,14 @@ EXPORT_SYMBOL_GPL(regulator_get_mode);
int regulator_set_load(struct regulator *regulator, int uA_load)
{
struct regulator_dev *rdev = regulator->rdev;
- int ret;
+ int ret = 0;
mutex_lock(&rdev->mutex);
regulator->uA_load = uA_load;
- ret = drms_uA_update(rdev);
+
+ if (!rdev->constraints->boot_protection)
+ ret = drms_uA_update(rdev);
+
mutex_unlock(&rdev->mutex);
return ret;
@@ -3344,7 +3386,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
if (enable && !regulator->bypass) {
rdev->bypass_count++;
- if (rdev->bypass_count == rdev->open_count) {
+ if (rdev->bypass_count == rdev->open_count &&
+ !rdev->constraints->boot_protection) {
ret = rdev->desc->ops->set_bypass(rdev, enable);
if (ret != 0)
rdev->bypass_count--;
@@ -3353,7 +3396,8 @@ int regulator_allow_bypass(struct regulator *regulator, bool enable)
} else if (!enable && regulator->bypass) {
rdev->bypass_count--;
- if (rdev->bypass_count != rdev->open_count) {
+ if (rdev->bypass_count != rdev->open_count &&
+ !rdev->constraints->boot_protection) {
ret = rdev->desc->ops->set_bypass(rdev, enable);
if (ret != 0)
rdev->bypass_count++;
@@ -4346,6 +4390,51 @@ static int __init regulator_init(void)
/* init early to allow our consumers to complete system booting */
core_initcall(regulator_init);
+static void __init regulator_clear_boot_protection(struct regulator_dev *rdev)
+{
+ struct regulator *regulator;
+ int min_uA = INT_MAX, max_uA = 0;
+
+ mutex_lock(&rdev->mutex);
+
+ rdev->constraints->boot_protection = 0;
+
+ /* update current setting */
+ list_for_each_entry(regulator, &rdev->consumer_list, list) {
+ if (regulator->min_uA < min_uA)
+ min_uA = regulator->min_uA;
+ if (regulator->max_uA > max_uA)
+ max_uA = regulator->max_uA;
+ }
+
+ if (max_uA && !regulator_check_current_limit(rdev, &min_uA, &max_uA))
+ rdev->desc->ops->set_current_limit(rdev, min_uA, max_uA);
+
+ /* constraints check has already done */
+ if (rdev->boot_mode)
+ rdev->desc->ops->set_mode(rdev, rdev->boot_mode);
+
+ /* update regulator load */
+ drms_uA_update(rdev);
+
+ /* check if we need to set bypass mode */
+ if (rdev->desc->ops->set_bypass && rdev->bypass_count &&
+ regulator_ops_is_valid(rdev, REGULATOR_CHANGE_BYPASS)) {
+ if (rdev->bypass_count == rdev->open_count)
+ rdev->desc->ops->set_bypass(rdev, true);
+ else
+ rdev->desc->ops->set_bypass(rdev, false);
+ }
+
+ regulator = list_first_entry_or_null(&rdev->consumer_list,
+ struct regulator, list);
+ mutex_unlock(&rdev->mutex);
+
+ if (regulator)
+ regulator_set_voltage(regulator, regulator->min_uV,
+ regulator->max_uV);
+}
+
static int __init regulator_late_cleanup(struct device *dev, void *data)
{
struct regulator_dev *rdev = dev_to_rdev(dev);
@@ -4353,6 +4442,9 @@ static int __init regulator_late_cleanup(struct device *dev, void *data)
struct regulation_constraints *c = rdev->constraints;
int enabled, ret;
+ if (c->boot_protection)
+ regulator_clear_boot_protection(rdev);
+
if (c && c->always_on)
return 0;
@@ -4406,6 +4498,8 @@ static int __init regulator_init_complete(void)
if (of_have_populated_dt())
has_full_constraints = true;
+ regulator_has_booted = true;
+
/* If we have a full configuration then disable any regulators
* we have permission to change the status for and which are
* not in use or always_on. This is effectively the default
@@ -29,6 +29,8 @@ struct regulator {
int uA_load;
int min_uV;
int max_uV;
+ int min_uA;
+ int max_uA;
char *supply_name;
struct device_attribute dev_attr;
struct regulator_dev *rdev;
@@ -78,6 +78,9 @@ static void of_get_regulation_constraints(struct device_node *np,
if (of_property_read_bool(np, "regulator-allow-set-load"))
constraints->valid_ops_mask |= REGULATOR_CHANGE_DRMS;
+ constraints->boot_protection = of_property_read_bool(np,
+ "regulator-boot-protection");
+
ret = of_property_read_u32(np, "regulator-ramp-delay", &pval);
if (!ret) {
if (pval)
@@ -389,6 +389,9 @@ struct regulator_dev {
u32 open_count;
u32 bypass_count;
+ /* save mode during boot protection */
+ unsigned int boot_mode;
+
/* lists we belong to */
struct list_head list; /* list of all regulators */
@@ -155,6 +155,7 @@ struct regulation_constraints {
/* constraint flags */
unsigned always_on:1; /* regulator never off when system is on */
unsigned boot_on:1; /* bootloader/firmware enabled regulator */
+ unsigned boot_protection:1; /* protect regulator initialized by bootloader */
unsigned apply_uV:1; /* apply uV constraint if min == max */
unsigned ramp_disable:1; /* disable ramp delay */
unsigned soft_start:1; /* ramp voltage slowly */
In some platforms, critical shared regulator is initialized in bootloader. But during kernel booting, the driver probing order and conflicting operations from other regulator consumers, may set the regulator in a undefined state, which will cause serious problem. This patch try to add a boot_protection flag in regulator constraints. And regulator core will postpone all operations until all consumers have taked their place. The boot_protection flag only work before late_initicall. And as other constraints liked, you can specify this flag in a board file, or in dts file. Signed-off-by: WEN Pingbo <pingbo.wen@linaro.org> Cc: Stephen Boyd <stephen.boyd@linaro.org> Cc: David Collins <collinsd@codeaurora.org> --- drivers/regulator/core.c | 106 +++++++++++++++++++++++++++++++++++--- drivers/regulator/internal.h | 2 + drivers/regulator/of_regulator.c | 3 ++ include/linux/regulator/driver.h | 3 ++ include/linux/regulator/machine.h | 1 + 5 files changed, 109 insertions(+), 6 deletions(-) -- 1.9.1