[PATCH RFC v3 4/5] regulator: add properties to handle monitoring on state change

From: Benjamin Bara
Date: Sun May 21 2023 - 07:47:42 EST


From: Benjamin Bara <benjamin.bara@xxxxxxxxxxx>

These are useful when the state of the regulator might change during
runtime, but the monitors state (in hardware) are not implicitly changed
with the change of the regulator state or mode (in hardware). Also, when
the monitors should be disabled while ramping after a set_value().

Signed-off-by: Benjamin Bara <benjamin.bara@xxxxxxxxxxx>
---
drivers/regulator/core.c | 90 ++++++++++++++++++++++++++++++++++++++++
include/linux/regulator/driver.h | 13 ++++++
2 files changed, 103 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index e59204920d6c..98a9283a0322 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -1638,6 +1638,34 @@ static int set_machine_constraints(struct regulator_dev *rdev)
}
}

+ /*
+ * when the core is in charge of disabling monitors while the regulator
+ * changes its value, it is essential to know if a monitor is active.
+ */
+ if (rdev->desc->mon_disable_reg_set_higher ||
+ rdev->desc->mon_disable_reg_set_lower) {
+ unsigned int monitor_state = REGULATOR_MONITOR_INVALID;
+
+ ret = ops->get_active_protections(rdev, &monitor_state);
+ if (ret)
+ return ret;
+
+ /*
+ * when a monitor is active, without dt knowing, we have to
+ * adapt the constraints to ensure functionality.
+ */
+ if (!rdev->constraints->over_voltage_detection &&
+ (monitor_state & REGULATOR_MONITOR_OVER_VOLTAGE)) {
+ rdev_warn(rdev, "dt unaware of over-voltage protection!\n");
+ rdev->constraints->over_voltage_detection = 1;
+ }
+ if (!rdev->constraints->under_voltage_detection &&
+ (monitor_state & REGULATOR_MONITOR_UNDER_VOLTAGE)) {
+ rdev_warn(rdev, "dt unaware of under-voltage protection!\n");
+ rdev->constraints->under_voltage_detection = 1;
+ }
+ }
+
/* set initial monitor state to current regulator state. */
ret = _regulator_is_enabled(rdev);
if (ret >= 0) {
@@ -3516,6 +3544,15 @@ static int _regulator_call_set_voltage(struct regulator_dev *rdev,
if (ret & NOTIFY_STOP_MASK)
return -EINVAL;

+ if ((rdev->desc->mon_disable_reg_set_higher &&
+ (min_uV > data.old_uV || max_uV > data.old_uV)) ||
+ (rdev->desc->mon_disable_reg_set_lower &&
+ (min_uV < data.old_uV || max_uV < data.old_uV))) {
+ ret = monitors_set_state(rdev, false);
+ if (ret)
+ return ret;
+ }
+
ret = rdev->desc->ops->set_voltage(rdev, min_uV, max_uV, selector);
if (ret >= 0)
return ret;
@@ -3540,6 +3577,13 @@ static int _regulator_call_set_voltage_sel(struct regulator_dev *rdev,
if (ret & NOTIFY_STOP_MASK)
return -EINVAL;

+ if ((rdev->desc->mon_disable_reg_set_higher && uV > data.old_uV) ||
+ (rdev->desc->mon_disable_reg_set_lower && uV < data.old_uV)) {
+ ret = monitors_set_state(rdev, false);
+ if (ret)
+ return ret;
+ }
+
ret = rdev->desc->ops->set_voltage_sel(rdev, selector);
if (ret >= 0)
return ret;
@@ -3736,6 +3780,15 @@ static int _regulator_do_set_voltage(struct regulator_dev *rdev,
out:
trace_regulator_set_voltage_complete(rdev_get_name(rdev), best_val);

+ if ((rdev->desc->mon_disable_reg_set_higher || rdev->desc->mon_disable_reg_set_lower) &&
+ _regulator_is_enabled(rdev) > 0) {
+ /* if setting voltage failed, ignore monitoring error. */
+ if (ret)
+ monitors_set_state(rdev, true);
+ else
+ ret = monitors_set_state(rdev, true);
+ }
+
return ret;
}

@@ -4645,7 +4698,24 @@ int regulator_set_mode(struct regulator *regulator, unsigned int mode)
if (ret < 0)
goto out;

+ if (mode & rdev->desc->mon_unsupported_reg_modes) {
+ ret = monitors_set_state(rdev, false);
+ if (ret)
+ goto out;
+ }
+
ret = rdev->desc->ops->set_mode(rdev, mode);
+ if (ret) {
+ /* get_mode() is required: regulator_curr_mode should be valid. */
+ if ((regulator_curr_mode & ~rdev->desc->mon_unsupported_reg_modes) &&
+ _regulator_is_enabled(rdev) > 0)
+ monitors_set_state(rdev, true);
+ goto out;
+ }
+
+ if ((mode & ~rdev->desc->mon_unsupported_reg_modes) && _regulator_is_enabled(rdev) > 0)
+ ret = monitors_set_state(rdev, true);
+
out:
regulator_unlock(rdev);
return ret;
@@ -5572,6 +5642,26 @@ regulator_register(struct device *dev,
goto rinse;
}

+ /*
+ * mon_unsupported_reg_modes property requires get_mode() to get the old
+ * state in case a state switch is failing.
+ */
+ if (regulator_desc->mon_unsupported_reg_modes &&
+ !regulator_desc->ops->get_mode) {
+ ret = -EINVAL;
+ goto rinse;
+ }
+ /*
+ * mon_disable_reg_set_* property requires get_active_protections() to
+ * know if a regulator is monitored without the device-tree knowing it.
+ */
+ if ((regulator_desc->mon_disable_reg_set_higher ||
+ regulator_desc->mon_disable_reg_set_lower) &&
+ !regulator_desc->ops->get_active_protections) {
+ ret = -EINVAL;
+ goto rinse;
+ }
+
rdev = kzalloc(sizeof(struct regulator_dev), GFP_KERNEL);
if (rdev == NULL) {
ret = -ENOMEM;
diff --git a/include/linux/regulator/driver.h b/include/linux/regulator/driver.h
index 35547e9eca48..bf42a5f452c5 100644
--- a/include/linux/regulator/driver.h
+++ b/include/linux/regulator/driver.h
@@ -366,6 +366,15 @@ enum regulator_type {
* the regulator was actually enabled. Max upto enable_time.
*
* @of_map_mode: Maps a hardware mode defined in a DeviceTree to a standard mode
+ *
+ * @mon_disable_reg_set_higher: Disables regulator's monitors while it is
+ * changing its value to a higher one.
+ * @mon_disable_reg_set_lower: Disables regulator's monitors while it is
+ * changing its value to a lower one.
+ * @mon_unsupported_reg_modes: Disables regulator's monitors before an
+ * unsupported mode is entered. REGULATOR_MODE_* are
+ * OR'ed. REGULATOR_MODE_INVALID means all modes can
+ * be monitored.
*/
struct regulator_desc {
const char *name;
@@ -440,6 +449,10 @@ struct regulator_desc {
unsigned int poll_enabled_time;

unsigned int (*of_map_mode)(unsigned int mode);
+
+ unsigned int mon_disable_reg_set_higher;
+ unsigned int mon_disable_reg_set_lower;
+ unsigned int mon_unsupported_reg_modes;
};

/**

--
2.34.1