Commit c1f615e4 authored by Mark Brown's avatar Mark Brown

Merge series "Fix regulators coupling for Exynos5800" from Marek Szyprowski...

Merge series "Fix regulators coupling for Exynos5800" from Marek Szyprowski <m.szyprowski@samsung.com>:

Hi!

This patchset is another attempt to fix the regulator coupling on
Exynos5800/5422 SoCs. Here are links to the previous attempts:

https://lore.kernel.org/linux-samsung-soc/20191008101709.qVNy8eijBi0LynOteWFMnTg4GUwKG599n6OyYoX1Abs@z/
https://lore.kernel.org/lkml/20191017102758.8104-1-m.szyprowski@samsung.com/
https://lore.kernel.org/linux-pm/cover.1589528491.git.viresh.kumar@linaro.org/
https://lore.kernel.org/linux-pm/20200528131130.17984-1-m.szyprowski@samsung.com/

The problem is with "vdd_int" regulator coupled with "vdd_arm" on Odroid
XU3/XU4 boards family. "vdd_arm" is handled by CPUfreq. "vdd_int" is
handled by devfreq. CPUfreq initialized quite early during boot and it
starts changing OPPs and "vdd_arm" value. Sometimes CPU activity during
boot goes down and some low-frequency OPPs are selected, what in turn
causes lowering "vdd_arm". This happens before devfreq applies its
requirements on "vdd_int". Regulator balancing code reduces "vdd_arm"
voltage value, what in turn causes lowering "vdd_int" value to the lowest
possible value. This is much below the operation point of the wcore bus,
which still runs at the highest frequency.

The issue was hard to notice because in the most cases the board managed
to boot properly, even when the regulator was set to lowest value allowed
by the regulator constraints. However, it caused some random issues,
which can be observed as "Unhandled prefetch abort" or low USB stability.

Adding more and more special cases to the generic code has been rejected,
so the only way to ensure the desired behavior on Exynos5800-based SoCs
is to make a custom regulator coupler driver.

Best regards,
Marek Szyprowski

Patch summary:

Marek Szyprowski (2):
  regulator: extract voltage balancing code to separate function
  soc: samsung: Add simple voltage coupler for Exynos5800

 arch/arm/mach-exynos/Kconfig                  |  1 +
 drivers/regulator/core.c                      | 49 ++++++++-------
 drivers/soc/samsung/Kconfig                   |  3 +
 drivers/soc/samsung/Makefile                  |  1 +
 .../soc/samsung/exynos-regulator-coupler.c    | 59 +++++++++++++++++++
 include/linux/regulator/coupler.h             |  8 +++
 6 files changed, 101 insertions(+), 20 deletions(-)
 create mode 100644 drivers/soc/samsung/exynos-regulator-coupler.c

--
2.17.1

base-commit: 8f3d9f35
parents 0c680ffb 752db83a
......@@ -3642,36 +3642,19 @@ static int regulator_get_optimal_voltage(struct regulator_dev *rdev,
return done;
}
static int regulator_balance_voltage(struct regulator_dev *rdev,
suspend_state_t state)
int regulator_do_balance_voltage(struct regulator_dev *rdev,
suspend_state_t state, bool skip_coupled)
{
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;
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;
/*
* If system is in a state other than PM_SUSPEND_ON, don't check
* other coupled regulators.
*/
if (state != PM_SUSPEND_ON)
n_coupled = 1;
if (c_desc->n_resolved < n_coupled) {
rdev_err(rdev, "Not all coupled regulators registered\n");
return -EPERM;
}
/* Invoke custom balancer for customized couplers */
if (coupler && coupler->balance_voltage)
return coupler->balance_voltage(coupler, rdev, state);
n_coupled = skip_coupled ? 1 : c_desc->n_coupled;
/*
* Find the best possible voltage change on each loop. Leave the loop
......@@ -3742,6 +3725,32 @@ static int regulator_balance_voltage(struct regulator_dev *rdev,
return ret;
}
static int regulator_balance_voltage(struct regulator_dev *rdev,
suspend_state_t state)
{
struct coupling_desc *c_desc = &rdev->coupling_desc;
struct regulator_coupler *coupler = c_desc->coupler;
bool skip_coupled = false;
/*
* If system is in a state other than PM_SUSPEND_ON, don't check
* other coupled regulators.
*/
if (state != PM_SUSPEND_ON)
skip_coupled = true;
if (c_desc->n_resolved < c_desc->n_coupled) {
rdev_err(rdev, "Not all coupled regulators registered\n");
return -EPERM;
}
/* Invoke custom balancer for customized couplers */
if (coupler && coupler->balance_voltage)
return coupler->balance_voltage(coupler, rdev, state);
return regulator_do_balance_voltage(rdev, state, skip_coupled);
}
/**
* regulator_set_voltage - set regulator output voltage
* @regulator: regulator source
......
......@@ -62,6 +62,8 @@ int regulator_get_voltage_rdev(struct regulator_dev *rdev);
int regulator_set_voltage_rdev(struct regulator_dev *rdev,
int min_uV, int max_uV,
suspend_state_t state);
int regulator_do_balance_voltage(struct regulator_dev *rdev,
suspend_state_t state, bool skip_coupled);
#else
static inline int regulator_coupler_register(struct regulator_coupler *coupler)
{
......@@ -92,6 +94,12 @@ static inline int regulator_set_voltage_rdev(struct regulator_dev *rdev,
{
return -EINVAL;
}
static inline int regulator_do_balance_voltage(struct regulator_dev *rdev,
suspend_state_t state,
bool skip_coupled)
{
return -EINVAL;
}
#endif
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment