Merge remote-tracking branch 'regulator/topic/coupled' into regulator-next
authorMark Brown <broonie@kernel.org>
Thu, 4 Jul 2019 16:34:34 +0000 (17:34 +0100)
committerMark Brown <broonie@kernel.org>
Thu, 4 Jul 2019 16:34:34 +0000 (17:34 +0100)
1  2 
drivers/regulator/core.c
drivers/regulator/of_regulator.c
include/linux/regulator/driver.h
include/linux/regulator/machine.h

diff --combined drivers/regulator/core.c
index df82e2a8442abaa83efc34ab1668f4ae22cc8b07,b9bc45128b8ca137c5d390defc5b83714b44be58..86ae1825cec1706f8949d3d4ac30fe9aa81971f3
@@@ -1,11 -1,17 +1,11 @@@
 -/*
 - * core.c  --  Voltage/Current Regulator framework.
 - *
 - * Copyright 2007, 2008 Wolfson Microelectronics PLC.
 - * Copyright 2008 SlimLogic Ltd.
 - *
 - * Author: Liam Girdwood <lrg@slimlogic.co.uk>
 - *
 - *  This program is free software; you can redistribute  it and/or modify it
 - *  under  the terms of  the GNU General  Public License as published by the
 - *  Free Software Foundation;  either version 2 of the  License, or (at your
 - *  option) any later version.
 - *
 - */
 +// SPDX-License-Identifier: GPL-2.0-or-later
 +//
 +// core.c  --  Voltage/Current Regulator framework.
 +//
 +// Copyright 2007, 2008 Wolfson Microelectronics PLC.
 +// Copyright 2008 SlimLogic Ltd.
 +//
 +// Author: Liam Girdwood <lrg@slimlogic.co.uk>
  
  #include <linux/kernel.h>
  #include <linux/init.h>
@@@ -22,6 -28,7 +22,7 @@@
  #include <linux/regmap.h>
  #include <linux/regulator/of_regulator.h>
  #include <linux/regulator/consumer.h>
+ #include <linux/regulator/coupler.h>
  #include <linux/regulator/driver.h>
  #include <linux/regulator/machine.h>
  #include <linux/module.h>
@@@ -49,6 -56,7 +50,7 @@@ static DEFINE_MUTEX(regulator_list_mute
  static LIST_HEAD(regulator_map_list);
  static LIST_HEAD(regulator_ena_gpio_list);
  static LIST_HEAD(regulator_supply_alias_list);
+ static LIST_HEAD(regulator_coupler_list);
  static bool has_full_constraints;
  
  static struct dentry *debugfs_root;
@@@ -92,7 -100,6 +94,6 @@@ struct regulator_supply_alias 
  
  static int _regulator_is_enabled(struct regulator_dev *rdev);
  static int _regulator_disable(struct regulator *regulator);
- static int _regulator_get_voltage(struct regulator_dev *rdev);
  static int _regulator_get_current_limit(struct regulator_dev *rdev);
  static unsigned int _regulator_get_mode(struct regulator_dev *rdev);
  static int _notifier_call_chain(struct regulator_dev *rdev,
@@@ -101,15 -108,12 +102,12 @@@ static int _regulator_do_set_voltage(st
                                     int min_uV, int max_uV);
  static int regulator_balance_voltage(struct regulator_dev *rdev,
                                     suspend_state_t state);
- static int regulator_set_voltage_rdev(struct regulator_dev *rdev,
-                                     int min_uV, int max_uV,
-                                     suspend_state_t state);
  static struct regulator *create_regulator(struct regulator_dev *rdev,
                                          struct device *dev,
                                          const char *supply_name);
  static void _regulator_put(struct regulator *regulator);
  
static const char *rdev_get_name(struct regulator_dev *rdev)
+ const char *rdev_get_name(struct regulator_dev *rdev)
  {
        if (rdev->constraints && rdev->constraints->name)
                return rdev->constraints->name;
@@@ -423,8 -427,8 +421,8 @@@ static struct device_node *of_get_regul
  }
  
  /* Platform voltage constraint check */
static int regulator_check_voltage(struct regulator_dev *rdev,
-                                  int *min_uV, int *max_uV)
+ int regulator_check_voltage(struct regulator_dev *rdev,
+                           int *min_uV, int *max_uV)
  {
        BUG_ON(*min_uV > *max_uV);
  
@@@ -456,9 -460,9 +454,9 @@@ static int regulator_check_states(suspe
  /* Make sure we select a voltage that suits the needs of all
   * regulator consumers
   */
static int regulator_check_consumers(struct regulator_dev *rdev,
-                                    int *min_uV, int *max_uV,
-                                    suspend_state_t state)
+ int regulator_check_consumers(struct regulator_dev *rdev,
+                             int *min_uV, int *max_uV,
+                             suspend_state_t state)
  {
        struct regulator *regulator;
        struct regulator_voltage *voltage;
@@@ -569,7 -573,7 +567,7 @@@ static ssize_t regulator_uV_show(struc
        ssize_t ret;
  
        regulator_lock(rdev);
-       ret = sprintf(buf, "%d\n", _regulator_get_voltage(rdev));
+       ret = sprintf(buf, "%d\n", regulator_get_voltage_rdev(rdev));
        regulator_unlock(rdev);
  
        return ret;
@@@ -940,7 -944,7 +938,7 @@@ static int drms_uA_update(struct regula
                        rdev_err(rdev, "failed to set load %d\n", current_uA);
        } else {
                /* get output voltage */
-               output_uV = _regulator_get_voltage(rdev);
+               output_uV = regulator_get_voltage_rdev(rdev);
                if (output_uV <= 0) {
                        rdev_err(rdev, "invalid output voltage found\n");
                        return -EINVAL;
@@@ -1053,7 -1057,7 +1051,7 @@@ static void print_constraints(struct re
  
        if (!constraints->min_uV ||
            constraints->min_uV != constraints->max_uV) {
-               ret = _regulator_get_voltage(rdev);
+               ret = regulator_get_voltage_rdev(rdev);
                if (ret > 0)
                        count += scnprintf(buf + count, len - count,
                                           "at %d mV ", ret / 1000);
@@@ -1112,7 -1116,7 +1110,7 @@@ static int machine_constraints_voltage(
        if (rdev->constraints->apply_uV &&
            rdev->constraints->min_uV && rdev->constraints->max_uV) {
                int target_min, target_max;
-               int current_uV = _regulator_get_voltage(rdev);
+               int current_uV = regulator_get_voltage_rdev(rdev);
  
                if (current_uV == -ENOTRECOVERABLE) {
                        /* This regulator can't be read and must be initialized */
                        _regulator_do_set_voltage(rdev,
                                                  rdev->constraints->min_uV,
                                                  rdev->constraints->max_uV);
-                       current_uV = _regulator_get_voltage(rdev);
+                       current_uV = regulator_get_voltage_rdev(rdev);
                }
  
                if (current_uV < 0) {
@@@ -1644,9 -1648,9 +1642,9 @@@ static int _regulator_get_enable_time(s
  {
        if (rdev->constraints && rdev->constraints->enable_time)
                return rdev->constraints->enable_time;
 -      if (!rdev->desc->ops->enable_time)
 -              return rdev->desc->enable_time;
 -      return rdev->desc->ops->enable_time(rdev);
 +      if (rdev->desc->ops->enable_time)
 +              return rdev->desc->ops->enable_time(rdev);
 +      return rdev->desc->enable_time;
  }
  
  static struct regulator_supply_alias *regulator_find_supply_alias(
@@@ -3064,7 -3068,7 +3062,7 @@@ static int _regulator_call_set_voltage(
        struct pre_voltage_change_data data;
        int ret;
  
-       data.old_uV = _regulator_get_voltage(rdev);
+       data.old_uV = regulator_get_voltage_rdev(rdev);
        data.min_uV = min_uV;
        data.max_uV = max_uV;
        ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
@@@ -3088,7 -3092,7 +3086,7 @@@ static int _regulator_call_set_voltage_
        struct pre_voltage_change_data data;
        int ret;
  
-       data.old_uV = _regulator_get_voltage(rdev);
+       data.old_uV = regulator_get_voltage_rdev(rdev);
        data.min_uV = uV;
        data.max_uV = uV;
        ret = _notifier_call_chain(rdev, REGULATOR_EVENT_PRE_VOLTAGE_CHANGE,
        return ret;
  }
  
 +static int _regulator_set_voltage_sel_step(struct regulator_dev *rdev,
 +                                         int uV, int new_selector)
 +{
 +      const struct regulator_ops *ops = rdev->desc->ops;
 +      int diff, old_sel, curr_sel, ret;
 +
 +      /* Stepping is only needed if the regulator is enabled. */
 +      if (!_regulator_is_enabled(rdev))
 +              goto final_set;
 +
 +      if (!ops->get_voltage_sel)
 +              return -EINVAL;
 +
 +      old_sel = ops->get_voltage_sel(rdev);
 +      if (old_sel < 0)
 +              return old_sel;
 +
 +      diff = new_selector - old_sel;
 +      if (diff == 0)
 +              return 0; /* No change needed. */
 +
 +      if (diff > 0) {
 +              /* Stepping up. */
 +              for (curr_sel = old_sel + rdev->desc->vsel_step;
 +                   curr_sel < new_selector;
 +                   curr_sel += rdev->desc->vsel_step) {
 +                      /*
 +                       * Call the callback directly instead of using
 +                       * _regulator_call_set_voltage_sel() as we don't
 +                       * want to notify anyone yet. Same in the branch
 +                       * below.
 +                       */
 +                      ret = ops->set_voltage_sel(rdev, curr_sel);
 +                      if (ret)
 +                              goto try_revert;
 +              }
 +      } else {
 +              /* Stepping down. */
 +              for (curr_sel = old_sel - rdev->desc->vsel_step;
 +                   curr_sel > new_selector;
 +                   curr_sel -= rdev->desc->vsel_step) {
 +                      ret = ops->set_voltage_sel(rdev, curr_sel);
 +                      if (ret)
 +                              goto try_revert;
 +              }
 +      }
 +
 +final_set:
 +      /* The final selector will trigger the notifiers. */
 +      return _regulator_call_set_voltage_sel(rdev, uV, new_selector);
 +
 +try_revert:
 +      /*
 +       * At least try to return to the previous voltage if setting a new
 +       * one failed.
 +       */
 +      (void)ops->set_voltage_sel(rdev, old_sel);
 +      return ret;
 +}
 +
  static int _regulator_set_voltage_time(struct regulator_dev *rdev,
                                       int old_uV, int new_uV)
  {
@@@ -3201,7 -3145,7 +3199,7 @@@ static int _regulator_do_set_voltage(st
        unsigned int selector;
        int old_selector = -1;
        const struct regulator_ops *ops = rdev->desc->ops;
-       int old_uV = _regulator_get_voltage(rdev);
+       int old_uV = regulator_get_voltage_rdev(rdev);
  
        trace_regulator_set_voltage(rdev_get_name(rdev), min_uV, max_uV);
  
                                best_val = ops->list_voltage(rdev,
                                                             selector);
                        else
-                               best_val = _regulator_get_voltage(rdev);
+                               best_val = regulator_get_voltage_rdev(rdev);
                }
  
        } else if (ops->set_voltage_sel) {
                                selector = ret;
                                if (old_selector == selector)
                                        ret = 0;
 +                              else if (rdev->desc->vsel_step)
 +                                      ret = _regulator_set_voltage_sel_step(
 +                                              rdev, best_val, selector);
                                else
                                        ret = _regulator_call_set_voltage_sel(
                                                rdev, best_val, selector);
@@@ -3350,7 -3291,7 +3348,7 @@@ static int regulator_set_voltage_unlock
         * changing the voltage.
         */
        if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
-               current_uV = _regulator_get_voltage(rdev);
+               current_uV = regulator_get_voltage_rdev(rdev);
                if (min_uV <= current_uV && current_uV <= max_uV) {
                        voltage->min_uV = min_uV;
                        voltage->max_uV = max_uV;
@@@ -3387,8 -3328,8 +3385,8 @@@ out
        return ret;
  }
  
static int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV,
-                                     int max_uV, suspend_state_t state)
+ int regulator_set_voltage_rdev(struct regulator_dev *rdev, int min_uV,
+                              int max_uV, suspend_state_t state)
  {
        int best_supply_uV = 0;
        int supply_change_uV = 0;
  
                best_supply_uV += rdev->desc->min_dropout_uV;
  
-               current_supply_uV = _regulator_get_voltage(rdev->supply->rdev);
+               current_supply_uV = regulator_get_voltage_rdev(rdev->supply->rdev);
                if (current_supply_uV < 0) {
                        ret = current_supply_uV;
                        goto out;
@@@ -3467,7 -3408,7 +3465,7 @@@ static int regulator_limit_voltage_step
                return 1;
  
        if (*current_uV < 0) {
-               *current_uV = _regulator_get_voltage(rdev);
+               *current_uV = regulator_get_voltage_rdev(rdev);
  
                if (*current_uV < 0)
                        return *current_uV;
@@@ -3496,11 -3437,10 +3494,10 @@@ static int regulator_get_optimal_voltag
        struct coupling_desc *c_desc = &rdev->coupling_desc;
        struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
        struct regulation_constraints *constraints = rdev->constraints;
-       int max_spread = constraints->max_spread;
        int desired_min_uV = 0, desired_max_uV = INT_MAX;
        int max_current_uV = 0, min_current_uV = INT_MAX;
        int highest_min_uV = 0, target_uV, possible_uV;
-       int i, ret;
+       int i, ret, max_spread;
        bool done;
  
        *current_uV = -1;
                }
        }
  
+       max_spread = constraints->max_spread[0];
        /*
         * Let target_uV be equal to the desired one if possible.
         * If not, set it to minimum voltage, allowed by other coupled
                if (!_regulator_is_enabled(c_rdevs[i]))
                        continue;
  
-               tmp_act = _regulator_get_voltage(c_rdevs[i]);
+               tmp_act = regulator_get_voltage_rdev(c_rdevs[i]);
                if (tmp_act < 0)
                        return tmp_act;
  
@@@ -3613,7 -3555,7 +3612,7 @@@ finish
        if (n_coupled > 1 && *current_uV == -1) {
  
                if (_regulator_is_enabled(rdev)) {
-                       ret = _regulator_get_voltage(rdev);
+                       ret = regulator_get_voltage_rdev(rdev);
                        if (ret < 0)
                                return ret;
  
@@@ -3635,9 -3577,11 +3634,11 @@@ static int regulator_balance_voltage(st
        struct regulator_dev **c_rdevs;
        struct regulator_dev *best_rdev;
        struct coupling_desc *c_desc = &rdev->coupling_desc;
+       struct regulator_coupler *coupler = c_desc->coupler;
        int i, ret, n_coupled, best_min_uV, best_max_uV, best_c_rdev;
-       bool best_c_rdev_done, c_rdev_done[MAX_COUPLED];
        unsigned int delta, best_delta;
+       unsigned long c_rdev_done = 0;
+       bool best_c_rdev_done;
  
        c_rdevs = c_desc->coupled_rdevs;
        n_coupled = c_desc->n_coupled;
                return -EPERM;
        }
  
-       for (i = 0; i < n_coupled; i++)
-               c_rdev_done[i] = false;
+       /* Invoke custom balancer for customized couplers */
+       if (coupler && coupler->balance_voltage)
+               return coupler->balance_voltage(coupler, rdev, state);
  
        /*
         * Find the best possible voltage change on each loop. Leave the loop
                         */
                        int optimal_uV = 0, optimal_max_uV = 0, current_uV = 0;
  
-                       if (c_rdev_done[i])
+                       if (test_bit(i, &c_rdev_done))
                                continue;
  
                        ret = regulator_get_optimal_voltage(c_rdevs[i],
                if (ret < 0)
                        goto out;
  
-               c_rdev_done[best_c_rdev] = best_c_rdev_done;
+               if (best_c_rdev_done)
+                       set_bit(best_c_rdev, &c_rdev_done);
  
        } while (n_coupled > 1);
  
@@@ -3973,7 -3919,7 +3976,7 @@@ out
  }
  EXPORT_SYMBOL_GPL(regulator_sync_voltage);
  
static int _regulator_get_voltage(struct regulator_dev *rdev)
int regulator_get_voltage_rdev(struct regulator_dev *rdev)
  {
        int sel, ret;
        bool bypassed;
                                return -EPROBE_DEFER;
                        }
  
-                       return _regulator_get_voltage(rdev->supply->rdev);
+                       return regulator_get_voltage_rdev(rdev->supply->rdev);
                }
        }
  
        } else if (rdev->desc->fixed_uV && (rdev->desc->n_voltages == 1)) {
                ret = rdev->desc->fixed_uV;
        } else if (rdev->supply) {
-               ret = _regulator_get_voltage(rdev->supply->rdev);
+               ret = regulator_get_voltage_rdev(rdev->supply->rdev);
        } else {
                return -EINVAL;
        }
@@@ -4031,7 -3977,7 +4034,7 @@@ int regulator_get_voltage(struct regula
        int ret;
  
        regulator_lock_dependent(regulator->rdev, &ww_ctx);
-       ret = _regulator_get_voltage(regulator->rdev);
+       ret = regulator_get_voltage_rdev(regulator->rdev);
        regulator_unlock_dependent(regulator->rdev, &ww_ctx);
  
        return ret;
@@@ -4769,8 -4715,60 +4772,60 @@@ static int regulator_register_resolve_s
        return 0;
  }
  
+ int regulator_coupler_register(struct regulator_coupler *coupler)
+ {
+       mutex_lock(&regulator_list_mutex);
+       list_add_tail(&coupler->list, &regulator_coupler_list);
+       mutex_unlock(&regulator_list_mutex);
+       return 0;
+ }
+ static struct regulator_coupler *
+ regulator_find_coupler(struct regulator_dev *rdev)
+ {
+       struct regulator_coupler *coupler;
+       int err;
+       /*
+        * Note that regulators are appended to the list and the generic
+        * coupler is registered first, hence it will be attached at last
+        * if nobody cared.
+        */
+       list_for_each_entry_reverse(coupler, &regulator_coupler_list, list) {
+               err = coupler->attach_regulator(coupler, rdev);
+               if (!err) {
+                       if (!coupler->balance_voltage &&
+                           rdev->coupling_desc.n_coupled > 2)
+                               goto err_unsupported;
+                       return coupler;
+               }
+               if (err < 0)
+                       return ERR_PTR(err);
+               if (err == 1)
+                       continue;
+               break;
+       }
+       return ERR_PTR(-EINVAL);
+ err_unsupported:
+       if (coupler->detach_regulator)
+               coupler->detach_regulator(coupler, rdev);
+       rdev_err(rdev,
+               "Voltage balancing for multiple regulator couples is unimplemented\n");
+       return ERR_PTR(-EPERM);
+ }
  static void regulator_resolve_coupling(struct regulator_dev *rdev)
  {
+       struct regulator_coupler *coupler = rdev->coupling_desc.coupler;
        struct coupling_desc *c_desc = &rdev->coupling_desc;
        int n_coupled = c_desc->n_coupled;
        struct regulator_dev *c_rdev;
                if (!c_rdev)
                        continue;
  
+               if (c_rdev->coupling_desc.coupler != coupler) {
+                       rdev_err(rdev, "coupler mismatch with %s\n",
+                                rdev_get_name(c_rdev));
+                       return;
+               }
                regulator_lock(c_rdev);
  
                c_desc->coupled_rdevs[i] = c_rdev;
  
  static void regulator_remove_coupling(struct regulator_dev *rdev)
  {
+       struct regulator_coupler *coupler = rdev->coupling_desc.coupler;
        struct coupling_desc *__c_desc, *c_desc = &rdev->coupling_desc;
        struct regulator_dev *__c_rdev, *c_rdev;
        unsigned int __n_coupled, n_coupled;
        int i, k;
+       int err;
  
        n_coupled = c_desc->n_coupled;
  
                c_desc->coupled_rdevs[i] = NULL;
                c_desc->n_resolved--;
        }
+       if (coupler && coupler->detach_regulator) {
+               err = coupler->detach_regulator(coupler, rdev);
+               if (err)
+                       rdev_err(rdev, "failed to detach from coupler: %d\n",
+                                err);
+       }
+       kfree(rdev->coupling_desc.coupled_rdevs);
+       rdev->coupling_desc.coupled_rdevs = NULL;
  }
  
  static int regulator_init_coupling(struct regulator_dev *rdev)
  {
-       int n_phandles;
+       int err, n_phandles;
+       size_t alloc_size;
  
        if (!IS_ENABLED(CONFIG_OF))
                n_phandles = 0;
        else
                n_phandles = of_get_n_coupled(rdev);
  
-       if (n_phandles + 1 > MAX_COUPLED) {
-               rdev_err(rdev, "too many regulators coupled\n");
-               return -EPERM;
-       }
+       alloc_size = sizeof(*rdev) * (n_phandles + 1);
+       rdev->coupling_desc.coupled_rdevs = kzalloc(alloc_size, GFP_KERNEL);
+       if (!rdev->coupling_desc.coupled_rdevs)
+               return -ENOMEM;
  
        /*
         * Every regulator should always have coupling descriptor filled with
        if (n_phandles == 0)
                return 0;
  
-       /* regulator, which can't change its voltage, can't be coupled */
-       if (!regulator_ops_is_valid(rdev, REGULATOR_CHANGE_VOLTAGE)) {
-               rdev_err(rdev, "voltage operation not allowed\n");
+       if (!of_check_coupling_data(rdev))
                return -EPERM;
-       }
  
-       if (rdev->constraints->max_spread <= 0) {
-               rdev_err(rdev, "wrong max_spread value\n");
-               return -EPERM;
+       rdev->coupling_desc.coupler = regulator_find_coupler(rdev);
+       if (IS_ERR(rdev->coupling_desc.coupler)) {
+               err = PTR_ERR(rdev->coupling_desc.coupler);
+               rdev_err(rdev, "failed to get coupler: %d\n", err);
+               return err;
        }
  
-       if (!of_check_coupling_data(rdev))
+       return 0;
+ }
+ static int generic_coupler_attach(struct regulator_coupler *coupler,
+                                 struct regulator_dev *rdev)
+ {
+       if (rdev->coupling_desc.n_coupled > 2) {
+               rdev_err(rdev,
+                        "Voltage balancing for multiple regulator couples is unimplemented\n");
                return -EPERM;
+       }
  
        return 0;
  }
  
+ static struct regulator_coupler generic_regulator_coupler = {
+       .attach_regulator = generic_coupler_attach,
+ };
  /**
   * regulator_register - register regulator
   * @regulator_desc: regulator to register
@@@ -5038,7 -5068,9 +5125,9 @@@ regulator_register(const struct regulat
        if (ret < 0)
                goto wash;
  
+       mutex_lock(&regulator_list_mutex);
        ret = regulator_init_coupling(rdev);
+       mutex_unlock(&regulator_list_mutex);
        if (ret < 0)
                goto wash;
  
  unset_supplies:
        mutex_lock(&regulator_list_mutex);
        unset_regulator_supplies(rdev);
+       regulator_remove_coupling(rdev);
        mutex_unlock(&regulator_list_mutex);
  wash:
        kfree(rdev->constraints);
@@@ -5340,7 -5373,7 +5430,7 @@@ static void regulator_summary_show_subt
                   rdev->use_count, rdev->open_count, rdev->bypass_count,
                   regulator_opmode_to_str(opmode));
  
-       seq_printf(s, "%5dmV ", _regulator_get_voltage(rdev) / 1000);
+       seq_printf(s, "%5dmV ", regulator_get_voltage_rdev(rdev) / 1000);
        seq_printf(s, "%5dmA ",
                   _regulator_get_current_limit_unlocked(rdev) / 1000);
  
@@@ -5542,6 -5575,8 +5632,8 @@@ static int __init regulator_init(void
  #endif
        regulator_dummy_init();
  
+       regulator_coupler_register(&generic_regulator_coupler);
        return ret;
  }
  
index 0ead1164e4d63afb52e4e41fddea86971ffcb334,db1cb2714b92a6c20ad1095cc59021f80772977f..397918ebba550a5c120481052ba586db7cb9f055
@@@ -1,9 -1,13 +1,9 @@@
 +// SPDX-License-Identifier: GPL-2.0-or-later
  /*
   * OF helpers for regulator framework
   *
   * Copyright (C) 2011 Texas Instruments, Inc.
   * Rajendra Nayak <rnayak@ti.com>
 - *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License as published by
 - * the Free Software Foundation; either version 2 of the License, or
 - * (at your option) any later version.
   */
  
  #include <linux/module.h>
@@@ -21,7 -25,8 +21,8 @@@ static const char *const regulator_stat
        [PM_SUSPEND_MAX]        = "regulator-state-disk",
  };
  
- static void of_get_regulation_constraints(struct device_node *np,
+ static int of_get_regulation_constraints(struct device *dev,
+                                       struct device_node *np,
                                        struct regulator_init_data **init_data,
                                        const struct regulator_desc *desc)
  {
        struct device_node *suspend_np;
        unsigned int mode;
        int ret, i, len;
+       int n_phandles;
        u32 pval;
  
+       n_phandles = of_count_phandle_with_args(np, "regulator-coupled-with",
+                                               NULL);
+       n_phandles = max(n_phandles, 0);
        constraints->name = of_get_property(np, "regulator-name", NULL);
  
        if (!of_property_read_u32(np, "regulator-min-microvolt", &pval))
        if (!of_property_read_u32(np, "regulator-system-load", &pval))
                constraints->system_load = pval;
  
-       if (!of_property_read_u32(np, "regulator-coupled-max-spread",
-                                 &pval))
-               constraints->max_spread = pval;
+       if (n_phandles) {
+               constraints->max_spread = devm_kzalloc(dev,
+                               sizeof(*constraints->max_spread) * n_phandles,
+                               GFP_KERNEL);
+               if (!constraints->max_spread)
+                       return -ENOMEM;
+               of_property_read_u32_array(np, "regulator-coupled-max-spread",
+                                          constraints->max_spread, n_phandles);
+       }
  
        if (!of_property_read_u32(np, "regulator-max-step-microvolt",
                                  &pval))
                suspend_state = NULL;
                suspend_np = NULL;
        }
+       return 0;
  }
  
  /**
@@@ -267,7 -287,9 +283,9 @@@ struct regulator_init_data *of_get_regu
        if (!init_data)
                return NULL; /* Out of memory? */
  
-       of_get_regulation_constraints(node, &init_data, desc);
+       if (of_get_regulation_constraints(dev, node, &init_data, desc))
+               return NULL;
        return init_data;
  }
  EXPORT_SYMBOL_GPL(of_get_regulator_init_data);
@@@ -473,7 -495,8 +491,8 @@@ int of_get_n_coupled(struct regulator_d
  
  /* Looks for "to_find" device_node in src's "regulator-coupled-with" property */
  static bool of_coupling_find_node(struct device_node *src,
-                                 struct device_node *to_find)
+                                 struct device_node *to_find,
+                                 int *index)
  {
        int n_phandles, i;
        bool found = false;
  
                of_node_put(tmp);
  
-               if (found)
+               if (found) {
+                       *index = i;
                        break;
+               }
        }
  
        return found;
   */
  bool of_check_coupling_data(struct regulator_dev *rdev)
  {
-       int max_spread = rdev->constraints->max_spread;
        struct device_node *node = rdev->dev.of_node;
        int n_phandles = of_get_n_coupled(rdev);
        struct device_node *c_node;
+       int index;
        int i;
        bool ret = true;
  
-       if (max_spread <= 0) {
-               dev_err(&rdev->dev, "max_spread value invalid\n");
-               return false;
-       }
        /* iterate over rdev's phandles */
        for (i = 0; i < n_phandles; i++) {
+               int max_spread = rdev->constraints->max_spread[i];
                int c_max_spread, c_n_phandles;
  
+               if (max_spread <= 0) {
+                       dev_err(&rdev->dev, "max_spread value invalid\n");
+                       return false;
+               }
                c_node = of_parse_phandle(node,
                                          "regulator-coupled-with", i);
  
                        goto clean;
                }
  
-               if (of_property_read_u32(c_node, "regulator-coupled-max-spread",
-                                        &c_max_spread)) {
+               if (!of_coupling_find_node(c_node, node, &index)) {
+                       dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n");
                        ret = false;
                        goto clean;
                }
  
-               if (c_max_spread != max_spread) {
-                       dev_err(&rdev->dev,
-                               "coupled regulators max_spread mismatch\n");
+               if (of_property_read_u32_index(c_node, "regulator-coupled-max-spread",
+                                              index, &c_max_spread)) {
                        ret = false;
                        goto clean;
                }
  
-               if (!of_coupling_find_node(c_node, node)) {
-                       dev_err(&rdev->dev, "missing 2-way linking for coupled regulators\n");
+               if (c_max_spread != max_spread) {
+                       dev_err(&rdev->dev,
+                               "coupled regulators max_spread mismatch\n");
                        ret = false;
+                       goto clean;
                }
  
  clean:
index 08259a75fecd24e448434e96f9aea2a5a3415c25,31b38a2b6995e3da295ec3936392babf086d0b0c..9a911bb5fb61aed06c3f026bb62fcbc0fe3525a5
@@@ -1,4 -1,3 +1,4 @@@
 +/* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * driver.h -- SoC Regulator driver support.
   *
@@@ -6,14 -5,16 +6,12 @@@
   *
   * Author: Liam Girdwood <lrg@slimlogic.co.uk>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License version 2 as
 - * published by the Free Software Foundation.
 - *
   * Regulator Driver Interface.
   */
  
  #ifndef __LINUX_REGULATOR_DRIVER_H_
  #define __LINUX_REGULATOR_DRIVER_H_
  
- #define MAX_COUPLED           2
  #include <linux/device.h>
  #include <linux/notifier.h>
  #include <linux/regulator/consumer.h>
@@@ -283,11 -284,6 +281,11 @@@ enum regulator_type 
   * @vsel_range_mask: Mask for register bitfield used for range selector
   * @vsel_reg: Register for selector when using regulator_regmap_X_voltage_
   * @vsel_mask: Mask for register bitfield used for selector
 + * @vsel_step: Specify the resolution of selector stepping when setting
 + *           voltage. If 0, then no stepping is done (requested selector is
 + *           set directly), if >0 then the regulator API will ramp the
 + *           voltage up/down gradually each time increasing/decreasing the
 + *           selector by the specified step value.
   * @csel_reg: Register for current limit selector using regmap set_current_limit
   * @csel_mask: Mask for register bitfield used for current limit selector
   * @apply_reg: Register for initiate voltage change on the output when
@@@ -362,7 -358,6 +360,7 @@@ struct regulator_desc 
        unsigned int vsel_range_mask;
        unsigned int vsel_reg;
        unsigned int vsel_mask;
 +      unsigned int vsel_step;
        unsigned int csel_reg;
        unsigned int csel_mask;
        unsigned int apply_reg;
@@@ -429,7 -424,8 +427,8 @@@ struct regulator_config 
   * incremented.
   */
  struct coupling_desc {
-       struct regulator_dev *coupled_rdevs[MAX_COUPLED];
+       struct regulator_dev **coupled_rdevs;
+       struct regulator_coupler *coupler;
        int n_resolved;
        int n_coupled;
  };
@@@ -555,4 -551,5 +554,5 @@@ void regulator_unlock(struct regulator_
   */
  int regulator_desc_list_voltage_linear_range(const struct regulator_desc *desc,
                                             unsigned int selector);
  #endif
index 5539efa76d2656c74b931e21abf3493a85404329,21db06e5c1edfe567399f417573f4dfc20b076a6..a84cc8879c3e086804b7ea02e19d150133d8cbb4
@@@ -1,4 -1,3 +1,4 @@@
 +/* SPDX-License-Identifier: GPL-2.0-only */
  /*
   * machine.h -- SoC Regulator support, machine/board driver API.
   *
@@@ -6,6 -5,10 +6,6 @@@
   *
   * Author: Liam Girdwood <lrg@slimlogic.co.uk>
   *
 - * This program is free software; you can redistribute it and/or modify
 - * it under the terms of the GNU General Public License version 2 as
 - * published by the Free Software Foundation.
 - *
   * Regulator Machine/Board Interface.
   */
  
@@@ -153,7 -156,7 +153,7 @@@ struct regulation_constraints 
        int system_load;
  
        /* used for coupled regulators */
-       int max_spread;
+       u32 *max_spread;
  
        /* used for changing voltage in steps */
        int max_uV_step;