Groups | Search | Server Info | Keyboard shortcuts | Login | Register [http] [https] [nntp] [nntps]


Groups > linux.kernel > #1411866 > unrolled thread

[RESEND][RFC PATCH] regulator: introduce boot protection flag

Started byWEN Pingbo <pingbo.wen@linaro.org>
First post2016-06-02 08:20 +0200
Last post2016-06-02 10:50 +0200
Articles 2 — 2 participants

Back to article view | Back to linux.kernel


Contents

  [RESEND][RFC PATCH] regulator: introduce boot protection flag WEN Pingbo <pingbo.wen@linaro.org> - 2016-06-02 08:20 +0200
    Re: [RESEND][RFC PATCH] regulator: introduce boot protection flag Mark Brown <broonie@kernel.org> - 2016-06-02 10:50 +0200

#1411866 — [RESEND][RFC PATCH] regulator: introduce boot protection flag

FromWEN Pingbo <pingbo.wen@linaro.org>
Date2016-06-02 08:20 +0200
Subject[RESEND][RFC PATCH] regulator: introduce boot protection flag
Message-ID<rFu93-2i2-1@gated-at.bofh.it>
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(-)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index fe47d38..f994a0f 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -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
diff --git a/drivers/regulator/internal.h b/drivers/regulator/internal.h
index c74ac87..ab81a71 100644
--- a/drivers/regulator/internal.h
+++ b/drivers/regulator/internal.h
@@ -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;
diff --git a/drivers/regulator/of_regulator.c b/drivers/regulator/of_regulator.c
index 6b0aa80..95fd789 100644
--- a/drivers/regulator/of_regulator.c
+++ b/drivers/regulator/of_regulator.c
@@ -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)
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index cd271e8..ddb80a3 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -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 */
 
diff --git a/include/linux/regulator/machine.h b/include/linux/regulator/machine.h
index 5d627c8..fd88a12 100644
--- a/include/linux/regulator/machine.h
+++ b/include/linux/regulator/machine.h
@@ -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 */
-- 
1.9.1

[toc] | [next] | [standalone]


#1412003

FromMark Brown <broonie@kernel.org>
Date2016-06-02 10:50 +0200
Message-ID<rFwue-3DX-9@gated-at.bofh.it>
In reply to#1411866

[Multipart message — attachments visible in raw view] — view raw

On Thu, Jun 02, 2016 at 02:16:14PM +0800, WEN Pingbo wrote:
> 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.

Please allow a reasonable time for review - you originally sent this
during the merge window or just before.

[toc] | [prev] | [standalone]


Back to top | Article view | linux.kernel


csiph-web