summaryrefslogtreecommitdiff
path: root/drivers/cpuidle/cpuidle-tegra.c
diff options
context:
space:
mode:
authorDmitry Osipenko <digetx@gmail.com>2020-02-25 01:40:53 +0300
committerThierry Reding <treding@nvidia.com>2020-03-13 13:32:01 +0300
commit19461a499c9e47d38f29e1ec4f0f614197cd67f2 (patch)
tree28e32966b8e723008b99f541f60a1003af39cf0b /drivers/cpuidle/cpuidle-tegra.c
parent860fbde438dc88d2fedf75965963b96c9041a0d5 (diff)
downloadlinux-19461a499c9e47d38f29e1ec4f0f614197cd67f2.tar.xz
cpuidle: tegra: Squash Tegra30 driver into the common driver
Tegra20 and Terga30 SoCs have common C1 and CC6 idling states and thus share the same code paths, there is no point in having separate drivers for a similar hardware. This patch merely moves functionality of the old driver into the new, although the CC6 state is kept disabled for now since old driver had a rudimentary support for this state (allowing to enter into CC6 only when secondary CPUs are put offline), while new driver can provide a full-featured support. The new feature will be enabled by another patch. Acked-by: Peter De Schrijver <pdeschrijver@nvidia.com> Tested-by: Peter Geis <pgwipeout@gmail.com> Tested-by: Jasper Korten <jja2000@gmail.com> Tested-by: David Heidelberg <david@ixit.cz> Tested-by: Nicolas Chauvet <kwizart@gmail.com> Acked-by: Daniel Lezcano <daniel.lezcano@linaro.org> Signed-off-by: Dmitry Osipenko <digetx@gmail.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
Diffstat (limited to 'drivers/cpuidle/cpuidle-tegra.c')
-rw-r--r--drivers/cpuidle/cpuidle-tegra.c74
1 files changed, 69 insertions, 5 deletions
diff --git a/drivers/cpuidle/cpuidle-tegra.c b/drivers/cpuidle/cpuidle-tegra.c
index 5691bdcf11cb..cd969ec18651 100644
--- a/drivers/cpuidle/cpuidle-tegra.c
+++ b/drivers/cpuidle/cpuidle-tegra.c
@@ -37,6 +37,7 @@
enum tegra_state {
TEGRA_C1,
+ TEGRA_C7,
TEGRA_CC6,
TEGRA_STATE_COUNT,
};
@@ -122,6 +123,11 @@ static int tegra_cpuidle_cc6_enter(unsigned int cpu)
return ret;
}
+static int tegra_cpuidle_c7_enter(void)
+{
+ return cpu_suspend(0, tegra30_pm_secondary_cpu_suspend);
+}
+
static int tegra_cpuidle_coupled_barrier(struct cpuidle_device *dev)
{
if (tegra_pending_sgi()) {
@@ -169,6 +175,10 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev,
cpu_pm_enter();
switch (index) {
+ case TEGRA_C7:
+ ret = tegra_cpuidle_c7_enter();
+ break;
+
case TEGRA_CC6:
ret = tegra_cpuidle_cc6_enter(cpu);
break;
@@ -185,6 +195,24 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev,
return ret;
}
+static int tegra_cpuidle_adjust_state_index(int index, unsigned int cpu)
+{
+ /*
+ * On Tegra30 CPU0 can't be power-gated separately from secondary
+ * cores because it gates the whole CPU cluster.
+ */
+ if (cpu > 0 || index != TEGRA_C7 || tegra_get_chip_id() != TEGRA30)
+ return index;
+
+ /* put CPU0 into C1 if C7 is requested and secondaries are online */
+ if (!IS_ENABLED(CONFIG_PM_SLEEP) || num_online_cpus() > 1)
+ index = TEGRA_C1;
+ else
+ index = TEGRA_CC6;
+
+ return index;
+}
+
static int tegra_cpuidle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
@@ -192,10 +220,17 @@ static int tegra_cpuidle_enter(struct cpuidle_device *dev,
unsigned int cpu = cpu_logical_map(dev->cpu);
int err;
- err = tegra_cpuidle_state_enter(dev, index, cpu);
- if (err && err != -EINTR)
- pr_err_once("cpu%u failed to enter idle state %d err: %d\n",
- cpu, index, err);
+ index = tegra_cpuidle_adjust_state_index(index, cpu);
+ if (dev->states_usage[index].disable)
+ return -1;
+
+ if (index == TEGRA_C1)
+ err = arm_cpuidle_simple_enter(dev, drv, index);
+ else
+ err = tegra_cpuidle_state_enter(dev, index, cpu);
+
+ if (err && (err != -EINTR || index != TEGRA_CC6))
+ pr_err_once("failed to enter state %d err: %d\n", index, err);
return err ? -1 : index;
}
@@ -221,6 +256,15 @@ static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.states = {
[TEGRA_C1] = ARM_CPUIDLE_WFI_STATE_PWR(600),
+ [TEGRA_C7] = {
+ .enter = tegra_cpuidle_enter,
+ .exit_latency = 2000,
+ .target_residency = 2200,
+ .power_usage = 100,
+ .flags = CPUIDLE_FLAG_TIMER_STOP,
+ .name = "C7",
+ .desc = "CPU core powered off",
+ },
[TEGRA_CC6] = {
.enter = tegra_cpuidle_enter,
.exit_latency = 5000,
@@ -265,8 +309,28 @@ static int tegra_cpuidle_probe(struct platform_device *pdev)
* Tegra-arch core and PMC driver, is unavailable if PM-sleep option
* is disabled.
*/
- if (!IS_ENABLED(CONFIG_PM_SLEEP))
+ if (!IS_ENABLED(CONFIG_PM_SLEEP)) {
+ tegra_cpuidle_disable_state(TEGRA_C7);
tegra_cpuidle_disable_state(TEGRA_CC6);
+ }
+
+ /*
+ * Generic WFI state (also known as C1 or LP3) and the coupled CPU
+ * cluster power-off (CC6 or LP2) states are common for all Tegra SoCs.
+ */
+ switch (tegra_get_chip_id()) {
+ case TEGRA20:
+ /* Tegra20 isn't capable to power-off individual CPU cores */
+ tegra_cpuidle_disable_state(TEGRA_C7);
+ break;
+
+ case TEGRA30:
+ tegra_cpuidle_disable_state(TEGRA_CC6);
+ break;
+
+ default:
+ return -EINVAL;
+ }
return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
}