[PATCH v5 4/5] regulator: core: Add voltage balancing mechanism

2018-03-02 Thread Maciej Purski
On Odroid XU3/4 and other Exynos5422 based boards there is a case, that
different devices on the board are supplied by different regulators
with non-fixed voltages. If one of these devices temporarily requires
higher voltage, there might occur a situation that the spread between
two devices' voltages is so high, that there is a risk of changing
'high' and 'low' states on the interconnection between devices powered
by those regulators.

Introduce new function regulator_balance_voltage(), which
keeps max_spread constraint fulfilled between a group of coupled
regulators. It should be called if a regulator changes its
voltage or after disabling or enabling. Disabled regulators should
follow changes of the enabled ones, but their consumers' demands
shouldn't be taken into account while calculating voltage of other
coupled regulators.

Find voltages, which are closest to suiting all the consumers' demands,
while fulfilling max_spread constraint, keeping the following rules:
- if one regulator is about to rise its voltage, rise others
  voltages in order to keep the max_spread
- if a regulator, which has caused rising other regulators, is
  lowered, lower other regulators if possible
- if one regulator is about to lower its voltage, but it hasn't caused
  rising other regulators, don't change its voltage if it breaks the
  max_spread

Change regulators' voltages step by step, keeping max_spread constraint
fulfilled all the time. Function regulator_get_optimal_voltage()
should find the best possible change for the regulator, which doesn't
break max_spread constraint. In function regulator_balance_voltage()
optimize number of steps by finding highest voltage difference on
each iteration.

If a regulator, which is about to change its voltage, is not coupled,
method regulator_get_optimal_voltage() should simply return the lowest
voltage fulfilling consumers' demands.

Coupling should be checked only if the system is in PM_SUSPEND_ON state.

Signed-off-by: Maciej Purski 
---
 drivers/regulator/core.c | 192 +++
 1 file changed, 192 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f8adfe4..b24a987 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -105,6 +105,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev,
  unsigned long event, void *data);
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
 int min_uV, int max_uV);
+static int regulator_balance_voltage(struct regulator_dev *rdev,
+suspend_state_t state);
 static struct regulator *create_regulator(struct regulator_dev *rdev,
  struct device *dev,
  const char *supply_name);
@@ -3040,6 +3042,196 @@ static int regulator_set_voltage_unlocked(struct 
regulator *regulator,
return ret;
 }
 
+static int regulator_get_optimal_voltage(struct regulator_dev *rdev)
+{
+   struct coupling_desc *c_desc = >coupling_desc;
+   struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
+   int max_spread = rdev->constraints->max_spread;
+   int n_coupled = c_desc->n_coupled;
+   int desired_min_uV, desired_max_uV, min_current_uV = INT_MAX;
+   int max_current_uV = 0, highest_min_uV = 0, target_uV, possible_uV;
+   int i, ret;
+
+   /* If consumers don't provide any demands, set voltage to min_uV */
+   desired_min_uV = rdev->constraints->min_uV;
+   desired_max_uV = rdev->constraints->max_uV;
+   ret = regulator_check_consumers(rdev,
+   _min_uV,
+   _max_uV, PM_SUSPEND_ON);
+   if (ret < 0)
+   goto out;
+
+   /*
+* If there are no coupled regulators, simply set the voltage demanded
+* by consumers.
+*/
+   if (n_coupled == 1) {
+   ret = desired_min_uV;
+   goto out;
+   }
+
+   /* Find highest min desired voltage */
+   for (i = 0; i < n_coupled; i++) {
+   int tmp_min = 0;
+   int tmp_max = INT_MAX;
+
+   if (!_regulator_is_enabled(c_rdevs[i]))
+   continue;
+
+   ret = regulator_check_consumers(c_rdevs[i],
+   _min,
+   _max, PM_SUSPEND_ON);
+   if (ret < 0)
+   goto out;
+
+   if (tmp_min > highest_min_uV)
+   highest_min_uV = tmp_min;
+   }
+
+   /*
+* Let target_uV be equal to the desired one if possible.
+* If not, set it to minimum voltage, allowed by other coupled
+* regulators.
+*/
+   target_uV = max(desired_min_uV,  highest_min_uV - max_spread);
+
+   /*
+* Find min 

[PATCH v5 4/5] regulator: core: Add voltage balancing mechanism

2018-03-02 Thread Maciej Purski
On Odroid XU3/4 and other Exynos5422 based boards there is a case, that
different devices on the board are supplied by different regulators
with non-fixed voltages. If one of these devices temporarily requires
higher voltage, there might occur a situation that the spread between
two devices' voltages is so high, that there is a risk of changing
'high' and 'low' states on the interconnection between devices powered
by those regulators.

Introduce new function regulator_balance_voltage(), which
keeps max_spread constraint fulfilled between a group of coupled
regulators. It should be called if a regulator changes its
voltage or after disabling or enabling. Disabled regulators should
follow changes of the enabled ones, but their consumers' demands
shouldn't be taken into account while calculating voltage of other
coupled regulators.

Find voltages, which are closest to suiting all the consumers' demands,
while fulfilling max_spread constraint, keeping the following rules:
- if one regulator is about to rise its voltage, rise others
  voltages in order to keep the max_spread
- if a regulator, which has caused rising other regulators, is
  lowered, lower other regulators if possible
- if one regulator is about to lower its voltage, but it hasn't caused
  rising other regulators, don't change its voltage if it breaks the
  max_spread

Change regulators' voltages step by step, keeping max_spread constraint
fulfilled all the time. Function regulator_get_optimal_voltage()
should find the best possible change for the regulator, which doesn't
break max_spread constraint. In function regulator_balance_voltage()
optimize number of steps by finding highest voltage difference on
each iteration.

If a regulator, which is about to change its voltage, is not coupled,
method regulator_get_optimal_voltage() should simply return the lowest
voltage fulfilling consumers' demands.

Coupling should be checked only if the system is in PM_SUSPEND_ON state.

Signed-off-by: Maciej Purski 
---
 drivers/regulator/core.c | 192 +++
 1 file changed, 192 insertions(+)

diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c
index f8adfe4..b24a987 100644
--- a/drivers/regulator/core.c
+++ b/drivers/regulator/core.c
@@ -105,6 +105,8 @@ static int _notifier_call_chain(struct regulator_dev *rdev,
  unsigned long event, void *data);
 static int _regulator_do_set_voltage(struct regulator_dev *rdev,
 int min_uV, int max_uV);
+static int regulator_balance_voltage(struct regulator_dev *rdev,
+suspend_state_t state);
 static struct regulator *create_regulator(struct regulator_dev *rdev,
  struct device *dev,
  const char *supply_name);
@@ -3040,6 +3042,196 @@ static int regulator_set_voltage_unlocked(struct 
regulator *regulator,
return ret;
 }
 
+static int regulator_get_optimal_voltage(struct regulator_dev *rdev)
+{
+   struct coupling_desc *c_desc = >coupling_desc;
+   struct regulator_dev **c_rdevs = c_desc->coupled_rdevs;
+   int max_spread = rdev->constraints->max_spread;
+   int n_coupled = c_desc->n_coupled;
+   int desired_min_uV, desired_max_uV, min_current_uV = INT_MAX;
+   int max_current_uV = 0, highest_min_uV = 0, target_uV, possible_uV;
+   int i, ret;
+
+   /* If consumers don't provide any demands, set voltage to min_uV */
+   desired_min_uV = rdev->constraints->min_uV;
+   desired_max_uV = rdev->constraints->max_uV;
+   ret = regulator_check_consumers(rdev,
+   _min_uV,
+   _max_uV, PM_SUSPEND_ON);
+   if (ret < 0)
+   goto out;
+
+   /*
+* If there are no coupled regulators, simply set the voltage demanded
+* by consumers.
+*/
+   if (n_coupled == 1) {
+   ret = desired_min_uV;
+   goto out;
+   }
+
+   /* Find highest min desired voltage */
+   for (i = 0; i < n_coupled; i++) {
+   int tmp_min = 0;
+   int tmp_max = INT_MAX;
+
+   if (!_regulator_is_enabled(c_rdevs[i]))
+   continue;
+
+   ret = regulator_check_consumers(c_rdevs[i],
+   _min,
+   _max, PM_SUSPEND_ON);
+   if (ret < 0)
+   goto out;
+
+   if (tmp_min > highest_min_uV)
+   highest_min_uV = tmp_min;
+   }
+
+   /*
+* Let target_uV be equal to the desired one if possible.
+* If not, set it to minimum voltage, allowed by other coupled
+* regulators.
+*/
+   target_uV = max(desired_min_uV,  highest_min_uV - max_spread);
+
+   /*
+* Find min and max voltages,