diff options
author | Mark Brown <broonie@kernel.org> | 2020-05-29 14:36:03 +0100 |
---|---|---|
committer | Mark Brown <broonie@kernel.org> | 2020-05-29 14:36:03 +0100 |
commit | c1f615e4f463b1620fff51d1c08a5e83c1683d6b (patch) | |
tree | 7d9cf500f2994c175d69ef58006a7a109b0b9bdc /drivers/regulator | |
parent | 0c680ffb99ea4022b97fbbf831da46b0f90958a5 (diff) | |
parent | 752db83a5dfd4fd3a0624b9ab440ed947fa003ca (diff) |
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: 8f3d9f354286745c751374f5f1fcafee6b3f3136
Diffstat (limited to 'drivers/regulator')
-rw-r--r-- | drivers/regulator/core.c | 49 |
1 files changed, 29 insertions, 20 deletions
diff --git a/drivers/regulator/core.c b/drivers/regulator/core.c index 941783a14b45..370c655ad8f6 100644 --- a/drivers/regulator/core.c +++ b/drivers/regulator/core.c @@ -3642,36 +3642,19 @@ finish: 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 @@ out: 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 |