Commit 19461a49 authored by Dmitry Osipenko's avatar Dmitry Osipenko Committed by Thierry Reding

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: default avatarPeter De Schrijver <pdeschrijver@nvidia.com>
Tested-by: default avatarPeter Geis <pgwipeout@gmail.com>
Tested-by: default avatarJasper Korten <jja2000@gmail.com>
Tested-by: default avatarDavid Heidelberg <david@ixit.cz>
Tested-by: default avatarNicolas Chauvet <kwizart@gmail.com>
Acked-by: default avatarDaniel Lezcano <daniel.lezcano@linaro.org>
Signed-off-by: default avatarDmitry Osipenko <digetx@gmail.com>
Signed-off-by: default avatarThierry Reding <treding@nvidia.com>
parent 860fbde4
...@@ -13,9 +13,6 @@ obj-y += sleep-tegra30.o ...@@ -13,9 +13,6 @@ obj-y += sleep-tegra30.o
obj-$(CONFIG_CPU_IDLE) += cpuidle.o obj-$(CONFIG_CPU_IDLE) += cpuidle.o
obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o obj-$(CONFIG_ARCH_TEGRA_2x_SOC) += pm-tegra20.o
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += pm-tegra30.o
ifeq ($(CONFIG_CPU_IDLE),y)
obj-$(CONFIG_ARCH_TEGRA_3x_SOC) += cpuidle-tegra30.o
endif
obj-$(CONFIG_SMP) += platsmp.o obj-$(CONFIG_SMP) += platsmp.o
obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o obj-$(CONFIG_HOTPLUG_CPU) += hotplug.o
......
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* CPU idle driver for Tegra CPUs
*
* Copyright (c) 2010-2012, NVIDIA Corporation.
* Copyright (c) 2011 Google, Inc.
* Author: Colin Cross <ccross@android.com>
* Gary King <gking@nvidia.com>
*
* Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
*/
#include <linux/clk/tegra.h>
#include <linux/tick.h>
#include <linux/cpuidle.h>
#include <linux/cpu_pm.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <soc/tegra/pm.h>
#include <asm/cpuidle.h>
#include <asm/smp_plat.h>
#include <asm/suspend.h>
#include "cpuidle.h"
#include "sleep.h"
#ifdef CONFIG_PM_SLEEP
static int tegra30_idle_lp2(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index);
#endif
static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle",
.owner = THIS_MODULE,
#ifdef CONFIG_PM_SLEEP
.state_count = 2,
#else
.state_count = 1,
#endif
.states = {
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
#ifdef CONFIG_PM_SLEEP
[1] = {
.enter = tegra30_idle_lp2,
.exit_latency = 2000,
.target_residency = 2200,
.power_usage = 0,
.flags = CPUIDLE_FLAG_TIMER_STOP,
.name = "powered-down",
.desc = "CPU power gated",
},
#endif
},
};
#ifdef CONFIG_PM_SLEEP
static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
/* All CPUs entering LP2 is not working.
* Don't let CPU0 enter LP2 when any secondary CPU is online.
*/
if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
cpu_do_idle();
return false;
}
return !tegra_pm_enter_lp2();
}
#ifdef CONFIG_SMP
static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
smp_wmb();
cpu_suspend(0, tegra30_pm_secondary_cpu_suspend);
return true;
}
#else
static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
return true;
}
#endif
static int tegra30_idle_lp2(struct cpuidle_device *dev,
struct cpuidle_driver *drv,
int index)
{
bool entered_lp2 = false;
local_fiq_disable();
tegra_pm_set_cpu_in_lp2();
cpu_pm_enter();
if (dev->cpu == 0)
entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv, index);
else
entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
cpu_pm_exit();
tegra_pm_clear_cpu_in_lp2();
local_fiq_enable();
return (entered_lp2) ? index : 0;
}
#endif
int __init tegra30_cpuidle_init(void)
{
return cpuidle_register(&tegra_idle_driver, NULL);
}
...@@ -24,11 +24,8 @@ void __init tegra_cpuidle_init(void) ...@@ -24,11 +24,8 @@ void __init tegra_cpuidle_init(void)
{ {
switch (tegra_get_chip_id()) { switch (tegra_get_chip_id()) {
case TEGRA20: case TEGRA20:
platform_device_register_simple("tegra-cpuidle", -1, NULL, 0);
break;
case TEGRA30: case TEGRA30:
if (IS_ENABLED(CONFIG_ARCH_TEGRA_3x_SOC)) platform_device_register_simple("tegra-cpuidle", -1, NULL, 0);
tegra30_cpuidle_init();
break; break;
case TEGRA114: case TEGRA114:
case TEGRA124: case TEGRA124:
......
...@@ -7,7 +7,6 @@ ...@@ -7,7 +7,6 @@
#define __MACH_TEGRA_CPUIDLE_H #define __MACH_TEGRA_CPUIDLE_H
#ifdef CONFIG_CPU_IDLE #ifdef CONFIG_CPU_IDLE
int tegra30_cpuidle_init(void);
int tegra114_cpuidle_init(void); int tegra114_cpuidle_init(void);
void tegra_cpuidle_init(void); void tegra_cpuidle_init(void);
#else #else
......
...@@ -37,6 +37,7 @@ ...@@ -37,6 +37,7 @@
enum tegra_state { enum tegra_state {
TEGRA_C1, TEGRA_C1,
TEGRA_C7,
TEGRA_CC6, TEGRA_CC6,
TEGRA_STATE_COUNT, TEGRA_STATE_COUNT,
}; };
...@@ -122,6 +123,11 @@ static int tegra_cpuidle_cc6_enter(unsigned int cpu) ...@@ -122,6 +123,11 @@ static int tegra_cpuidle_cc6_enter(unsigned int cpu)
return ret; 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) static int tegra_cpuidle_coupled_barrier(struct cpuidle_device *dev)
{ {
if (tegra_pending_sgi()) { if (tegra_pending_sgi()) {
...@@ -169,6 +175,10 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev, ...@@ -169,6 +175,10 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev,
cpu_pm_enter(); cpu_pm_enter();
switch (index) { switch (index) {
case TEGRA_C7:
ret = tegra_cpuidle_c7_enter();
break;
case TEGRA_CC6: case TEGRA_CC6:
ret = tegra_cpuidle_cc6_enter(cpu); ret = tegra_cpuidle_cc6_enter(cpu);
break; break;
...@@ -185,6 +195,24 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev, ...@@ -185,6 +195,24 @@ static int tegra_cpuidle_state_enter(struct cpuidle_device *dev,
return ret; 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, static int tegra_cpuidle_enter(struct cpuidle_device *dev,
struct cpuidle_driver *drv, struct cpuidle_driver *drv,
int index) int index)
...@@ -192,10 +220,17 @@ static int tegra_cpuidle_enter(struct cpuidle_device *dev, ...@@ -192,10 +220,17 @@ static int tegra_cpuidle_enter(struct cpuidle_device *dev,
unsigned int cpu = cpu_logical_map(dev->cpu); unsigned int cpu = cpu_logical_map(dev->cpu);
int err; int err;
err = tegra_cpuidle_state_enter(dev, index, cpu); index = tegra_cpuidle_adjust_state_index(index, cpu);
if (err && err != -EINTR) if (dev->states_usage[index].disable)
pr_err_once("cpu%u failed to enter idle state %d err: %d\n", return -1;
cpu, index, err);
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; return err ? -1 : index;
} }
...@@ -221,6 +256,15 @@ static struct cpuidle_driver tegra_idle_driver = { ...@@ -221,6 +256,15 @@ static struct cpuidle_driver tegra_idle_driver = {
.name = "tegra_idle", .name = "tegra_idle",
.states = { .states = {
[TEGRA_C1] = ARM_CPUIDLE_WFI_STATE_PWR(600), [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] = { [TEGRA_CC6] = {
.enter = tegra_cpuidle_enter, .enter = tegra_cpuidle_enter,
.exit_latency = 5000, .exit_latency = 5000,
...@@ -265,8 +309,28 @@ static int tegra_cpuidle_probe(struct platform_device *pdev) ...@@ -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 * Tegra-arch core and PMC driver, is unavailable if PM-sleep option
* is disabled. * 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); 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); return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
} }
......
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