aboutsummaryrefslogtreecommitdiff
path: root/drivers/soc/tegra/regulators-tegra30.c
diff options
context:
space:
mode:
authorGravatar Linus Torvalds <torvalds@linux-foundation.org> 2021-07-10 09:22:44 -0700
committerGravatar Linus Torvalds <torvalds@linux-foundation.org> 2021-07-10 09:22:44 -0700
commit6e207b882159ed3e35a4cd4ff0fc155cce5e3cbc (patch)
treeb33ae527de0082a3448365d8bc51f4fe8338fedd /drivers/soc/tegra/regulators-tegra30.c
parentMerge tag 'fixes-2021-07-09' of git://git.kernel.org/pub/scm/linux/kernel/git... (diff)
parentMerge branch 'arm/fixes' into arm/soc (diff)
downloadlinux-6e207b882159ed3e35a4cd4ff0fc155cce5e3cbc.tar.gz
linux-6e207b882159ed3e35a4cd4ff0fc155cce5e3cbc.tar.bz2
linux-6e207b882159ed3e35a4cd4ff0fc155cce5e3cbc.zip
Merge tag 'arm-soc-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc
Pull ARM SoC updates from Olof Johansson: "A few SoC (code) changes have queued up this cycle, mostly for minor changes and some refactoring and cleanup of legacy platforms. This branch also contains a few of the fixes that weren't sent in by the end of the release (all fairly minor). - Adding an additional maintainer for the TEE subsystem (Sumit Garg) - Quite a significant modernization of the IXP4xx platforms by Linus Walleij, revisiting with a new PCI host driver/binding, removing legacy mach/* include dependencies and moving platform detection/config to drivers/soc. Also some updates/cleanup of platform data. - Core power domain support for Tegra platforms, and some improvements in build test coverage by adding stubs for compile test targets. - A handful of updates to i.MX platforms, adding legacy (non-PSCI) SMP support on i.MX7D, SoC ID setup for i.MX50, removal of platform data and board fixups for iMX6/7. ... and a few smaller changes and fixes for Samsung, OMAP, Allwinner, Rockchip" * tag 'arm-soc-5.14' of git://git.kernel.org/pub/scm/linux/kernel/git/soc/soc: (53 commits) MAINTAINERS: Add myself as TEE subsystem reviewer ixp4xx: fix spelling mistake in Kconfig "Devce" -> "Device" hw_random: ixp4xx: Add OF support hw_random: ixp4xx: Add DT bindings hw_random: ixp4xx: Turn into a module hw_random: ixp4xx: Use SPDX license tag hw_random: ixp4xx: enable compile-testing pata: ixp4xx: split platform data to its own header soc: ixp4xx: move cpu detection to linux/soc/ixp4xx/cpu.h PCI: ixp4xx: Add a new driver for IXP4xx PCI: ixp4xx: Add device tree bindings for IXP4xx ARM/ixp4xx: Make NEED_MACH_IO_H optional ARM/ixp4xx: Move the virtual IObases MAINTAINERS: ARM/MStar/Sigmastar SoCs: Add a link to the MStar tree ARM: debug: add UART early console support for MSTAR SoCs ARM: dts: ux500: Fix LED probing ARM: imx: add smp support for imx7d ARM: imx6q: drop of_platform_default_populate() from init_machine arm64: dts: rockchip: Update RK3399 PCI host bridge window to 32-bit address memory soc/tegra: fuse: Fix Tegra234-only builds ...
Diffstat (limited to 'drivers/soc/tegra/regulators-tegra30.c')
-rw-r--r--drivers/soc/tegra/regulators-tegra30.c93
1 files changed, 91 insertions, 2 deletions
diff --git a/drivers/soc/tegra/regulators-tegra30.c b/drivers/soc/tegra/regulators-tegra30.c
index 0e776b20f625..e74bbc9c7859 100644
--- a/drivers/soc/tegra/regulators-tegra30.c
+++ b/drivers/soc/tegra/regulators-tegra30.c
@@ -12,17 +12,22 @@
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/of.h>
+#include <linux/reboot.h>
#include <linux/regulator/coupler.h>
#include <linux/regulator/driver.h>
#include <linux/regulator/machine.h>
#include <soc/tegra/fuse.h>
+#include <soc/tegra/pmc.h>
struct tegra_regulator_coupler {
struct regulator_coupler coupler;
struct regulator_dev *core_rdev;
struct regulator_dev *cpu_rdev;
- int core_min_uV;
+ struct notifier_block reboot_notifier;
+ int core_min_uV, cpu_min_uV;
+ bool sys_reboot_mode_req;
+ bool sys_reboot_mode;
};
static inline struct tegra_regulator_coupler *
@@ -39,6 +44,21 @@ static int tegra30_core_limit(struct tegra_regulator_coupler *tegra,
int core_cur_uV;
int err;
+ /*
+ * Tegra30 SoC has critical DVFS-capable devices that are
+ * permanently-active or active at a boot time, like EMC
+ * (DRAM controller) or Display controller for example.
+ *
+ * The voltage of a CORE SoC power domain shall not be dropped below
+ * a minimum level, which is determined by device's clock rate.
+ * This means that we can't fully allow CORE voltage scaling until
+ * the state of all DVFS-critical CORE devices is synced.
+ */
+ if (tegra_pmc_core_domain_state_synced() && !tegra->sys_reboot_mode) {
+ pr_info_once("voltage state synced\n");
+ return 0;
+ }
+
if (tegra->core_min_uV > 0)
return tegra->core_min_uV;
@@ -59,7 +79,7 @@ static int tegra30_core_limit(struct tegra_regulator_coupler *tegra,
*/
tegra->core_min_uV = core_max_uV;
- pr_info("core minimum voltage limited to %duV\n", tegra->core_min_uV);
+ pr_info("core voltage initialized to %duV\n", tegra->core_min_uV);
return tegra->core_min_uV;
}
@@ -172,6 +192,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
if (cpu_uV < 0)
return cpu_uV;
+ /* store boot voltage level */
+ if (!tegra->cpu_min_uV)
+ tegra->cpu_min_uV = cpu_uV;
+
/*
* CPU's regulator may not have any consumers, hence the voltage
* must not be changed in that case because CPU simply won't
@@ -195,6 +219,10 @@ static int tegra30_voltage_update(struct tegra_regulator_coupler *tegra,
if (err)
return err;
+ /* restore boot voltage level */
+ if (tegra->sys_reboot_mode)
+ cpu_min_uV = max(cpu_min_uV, tegra->cpu_min_uV);
+
if (core_min_limited_uV > core_uV) {
pr_err("core voltage constraint violated: %d %d %d\n",
core_uV, core_min_limited_uV, cpu_uV);
@@ -263,9 +291,56 @@ static int tegra30_regulator_balance_voltage(struct regulator_coupler *coupler,
return -EINVAL;
}
+ tegra->sys_reboot_mode = READ_ONCE(tegra->sys_reboot_mode_req);
+
return tegra30_voltage_update(tegra, cpu_rdev, core_rdev);
}
+static int tegra30_regulator_prepare_reboot(struct tegra_regulator_coupler *tegra,
+ bool sys_reboot_mode)
+{
+ int err;
+
+ if (!tegra->core_rdev || !tegra->cpu_rdev)
+ return 0;
+
+ WRITE_ONCE(tegra->sys_reboot_mode_req, true);
+
+ /*
+ * Some devices use CPU soft-reboot method and in this case we
+ * should ensure that voltages are sane for the reboot by restoring
+ * the minimum boot levels.
+ */
+ err = regulator_sync_voltage_rdev(tegra->cpu_rdev);
+ if (err)
+ return err;
+
+ err = regulator_sync_voltage_rdev(tegra->core_rdev);
+ if (err)
+ return err;
+
+ WRITE_ONCE(tegra->sys_reboot_mode_req, sys_reboot_mode);
+
+ return 0;
+}
+
+static int tegra30_regulator_reboot(struct notifier_block *notifier,
+ unsigned long event, void *cmd)
+{
+ struct tegra_regulator_coupler *tegra;
+ int ret;
+
+ if (event != SYS_RESTART)
+ return NOTIFY_DONE;
+
+ tegra = container_of(notifier, struct tegra_regulator_coupler,
+ reboot_notifier);
+
+ ret = tegra30_regulator_prepare_reboot(tegra, true);
+
+ return notifier_from_errno(ret);
+}
+
static int tegra30_regulator_attach(struct regulator_coupler *coupler,
struct regulator_dev *rdev)
{
@@ -292,6 +367,14 @@ static int tegra30_regulator_detach(struct regulator_coupler *coupler,
{
struct tegra_regulator_coupler *tegra = to_tegra_coupler(coupler);
+ /*
+ * We don't expect regulators to be decoupled during reboot,
+ * this may race with the reboot handler and shouldn't ever
+ * happen in practice.
+ */
+ if (WARN_ON_ONCE(system_state > SYSTEM_RUNNING))
+ return -EPERM;
+
if (tegra->core_rdev == rdev) {
tegra->core_rdev = NULL;
return 0;
@@ -311,13 +394,19 @@ static struct tegra_regulator_coupler tegra30_coupler = {
.detach_regulator = tegra30_regulator_detach,
.balance_voltage = tegra30_regulator_balance_voltage,
},
+ .reboot_notifier.notifier_call = tegra30_regulator_reboot,
};
static int __init tegra_regulator_coupler_init(void)
{
+ int err;
+
if (!of_machine_is_compatible("nvidia,tegra30"))
return 0;
+ err = register_reboot_notifier(&tegra30_coupler.reboot_notifier);
+ WARN_ON(err);
+
return regulator_coupler_register(&tegra30_coupler.coupler);
}
arch_initcall(tegra_regulator_coupler_init);