]> git.ipfire.org Git - thirdparty/kernel/linux.git/commitdiff
regulator: core: clamp voltage constraints before applying apply_uV
authorKamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Mon, 20 Jul 2026 15:55:17 +0000 (21:25 +0530)
committerMark Brown <broonie@kernel.org>
Tue, 21 Jul 2026 21:35:08 +0000 (22:35 +0100)
machine_constraints_voltage() currently applies apply_uV against the
machine-supplied [min_uV, max_uV] range, and only afterwards clamps
that range down to what the regulator can actually supply (via
ops->list_voltage()).

If the machine-supplied range is wider than the regulator's actual
range, apply_uV's rounding can pick a selector outside the (correct)
clamped range, so the regulator ends up programmed outside its clamped
min/max. At bring-up this shows up as a voltage read-back outside the
clamped range.

Fix this by moving the clamping block ahead of the apply_uV block, so
apply_uV always targets an already-clamped range. Whether apply_uV
should run is decided from the unclamped constraints beforehand and
stored in a local bool, since clamping must not itself change whether
apply_uV fires.

No functional change to the clamping logic itself, only its position
relative to apply_uV. Its early return 0 exits become fallthroughs
since the apply_uV logic now follows it.

Assisted-by: Claude:claude-sonnet-5
Signed-off-by: Kamal Wadhwa <kamal.wadhwa@oss.qualcomm.com>
Link: https://patch.msgid.link/20260720-b4-regulator-core-clamp-voltage-v1-1-8e5eec076a8e@oss.qualcomm.com
Signed-off-by: Mark Brown <broonie@kernel.org>
drivers/regulator/core.c

index 1797929dfe56632e0fdda9e323c0e0c16c217d38..2e61606fc1d05764b0ec13844feec354b1c07c2e 100644 (file)
@@ -1220,10 +1220,98 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
 {
        const struct regulator_ops *ops = rdev->desc->ops;
        int ret;
+       bool apply_uV;
+
+       /*
+        * Decide up front, from the constraints as handed to us, whether
+        * apply_uV needs to run below. The clamping pass right after this
+        * may rewrite constraints->min_uV/max_uV (e.g. the fixed-voltage
+        * autoconfigure case), and we don't want that to change whether
+        * apply_uV fires.
+        */
+       apply_uV = rdev->constraints->apply_uV &&
+                  rdev->constraints->min_uV && rdev->constraints->max_uV;
+
+       /*
+        * Constrain machine-level voltage specs to fit the actual range
+        * supported by this regulator before apply_uV (below) tries to
+        * force hardware to a value from that range: otherwise apply_uV
+        * can target a constraint value that doesn't correspond to any
+        * real voltage selector and fail registration outright, even
+        * though the clamping pass would have narrowed it to a value
+        * the regulator can actually hit.
+        */
+       if (ops->list_voltage && rdev->desc->n_voltages) {
+               int     count = rdev->desc->n_voltages;
+               int     i;
+               int     min_uV = INT_MAX;
+               int     max_uV = INT_MIN;
+               int     cmin = constraints->min_uV;
+               int     cmax = constraints->max_uV;
+
+               /* it's safe to autoconfigure fixed-voltage supplies
+                * and the constraints are used by list_voltage.
+                */
+               if (count == 1 && !cmin) {
+                       cmin = 1;
+                       cmax = INT_MAX;
+                       constraints->min_uV = cmin;
+                       constraints->max_uV = cmax;
+               }
+
+               /* voltage constraints are optional */
+               if ((cmin == 0) && (cmax == 0)) {
+                       /* nothing more to do */
+
+               /* else require explicit machine-level constraints */
+               } else if (cmin <= 0 || cmax <= 0 || cmax < cmin) {
+                       rdev_err(rdev, "invalid voltage constraints\n");
+                       return -EINVAL;
+
+               /* no need to loop voltages if range is continuous */
+               } else if (rdev->desc->continuous_voltage_range) {
+                       /* nothing more to do */
+
+               } else {
+                       /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */
+                       for (i = 0; i < count; i++) {
+                               int     value;
+
+                               value = ops->list_voltage(rdev, i);
+                               if (value <= 0)
+                                       continue;
+
+                               /* maybe adjust [min_uV..max_uV] */
+                               if (value >= cmin && value < min_uV)
+                                       min_uV = value;
+                               if (value <= cmax && value > max_uV)
+                                       max_uV = value;
+                       }
+
+                       /* final: [min_uV..max_uV] valid iff constraints valid */
+                       if (max_uV < min_uV) {
+                               rdev_err(rdev,
+                                        "unsupportable voltage constraints %u-%uuV\n",
+                                        min_uV, max_uV);
+                               return -EINVAL;
+                       }
+
+                       /* use regulator's subset of machine constraints */
+                       if (constraints->min_uV < min_uV) {
+                               rdev_dbg(rdev, "override min_uV, %d -> %d\n",
+                                        constraints->min_uV, min_uV);
+                               constraints->min_uV = min_uV;
+                       }
+                       if (constraints->max_uV > max_uV) {
+                               rdev_dbg(rdev, "override max_uV, %d -> %d\n",
+                                        constraints->max_uV, max_uV);
+                               constraints->max_uV = max_uV;
+                       }
+               }
+       }
 
        /* do we need to apply the constraint voltage */
-       if (rdev->constraints->apply_uV &&
-           rdev->constraints->min_uV && rdev->constraints->max_uV) {
+       if (apply_uV) {
                int target_min, target_max;
                int current_uV = regulator_get_voltage_rdev(rdev);
 
@@ -1278,77 +1366,6 @@ static int machine_constraints_voltage(struct regulator_dev *rdev,
                }
        }
 
-       /* constrain machine-level voltage specs to fit
-        * the actual range supported by this regulator.
-        */
-       if (ops->list_voltage && rdev->desc->n_voltages) {
-               int     count = rdev->desc->n_voltages;
-               int     i;
-               int     min_uV = INT_MAX;
-               int     max_uV = INT_MIN;
-               int     cmin = constraints->min_uV;
-               int     cmax = constraints->max_uV;
-
-               /* it's safe to autoconfigure fixed-voltage supplies
-                * and the constraints are used by list_voltage.
-                */
-               if (count == 1 && !cmin) {
-                       cmin = 1;
-                       cmax = INT_MAX;
-                       constraints->min_uV = cmin;
-                       constraints->max_uV = cmax;
-               }
-
-               /* voltage constraints are optional */
-               if ((cmin == 0) && (cmax == 0))
-                       return 0;
-
-               /* else require explicit machine-level constraints */
-               if (cmin <= 0 || cmax <= 0 || cmax < cmin) {
-                       rdev_err(rdev, "invalid voltage constraints\n");
-                       return -EINVAL;
-               }
-
-               /* no need to loop voltages if range is continuous */
-               if (rdev->desc->continuous_voltage_range)
-                       return 0;
-
-               /* initial: [cmin..cmax] valid, [min_uV..max_uV] not */
-               for (i = 0; i < count; i++) {
-                       int     value;
-
-                       value = ops->list_voltage(rdev, i);
-                       if (value <= 0)
-                               continue;
-
-                       /* maybe adjust [min_uV..max_uV] */
-                       if (value >= cmin && value < min_uV)
-                               min_uV = value;
-                       if (value <= cmax && value > max_uV)
-                               max_uV = value;
-               }
-
-               /* final: [min_uV..max_uV] valid iff constraints valid */
-               if (max_uV < min_uV) {
-                       rdev_err(rdev,
-                                "unsupportable voltage constraints %u-%uuV\n",
-                                min_uV, max_uV);
-                       return -EINVAL;
-               }
-
-               /* use regulator's subset of machine constraints */
-               if (constraints->min_uV < min_uV) {
-                       rdev_dbg(rdev, "override min_uV, %d -> %d\n",
-                                constraints->min_uV, min_uV);
-                       constraints->min_uV = min_uV;
-               }
-               if (constraints->max_uV > max_uV) {
-                       rdev_dbg(rdev, "override max_uV, %d -> %d\n",
-                                constraints->max_uV, max_uV);
-                       constraints->max_uV = max_uV;
-               }
-       }
-
        return 0;
 }