1ec0e115f8
This callback is expected to do the same as enter() but it has to guarantee that interrupts aren't enabled at any point in its execution, as the tick is frozen. It will be called when the system goes to suspend-to-idle and will reduce power usage because CPUs won't be awaken for unnecessary IRQs. By setting the CPUIDLE_FLAG_TIMER_STOP flag, we can reuse the same code for both the enter() and enter_freeze() callbacks. Signed-off-by: Tomeu Vizoso <tomeu.vizoso@collabora.com> Cc: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Thierry Reding <treding@nvidia.com>
98 lines
2.3 KiB
C
98 lines
2.3 KiB
C
/*
|
|
* Copyright (c) 2013, NVIDIA Corporation. All rights reserved.
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms and conditions of the GNU General Public License,
|
|
* version 2, as published by the Free Software Foundation.
|
|
*
|
|
* This program is distributed in the hope it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
|
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
|
* more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <asm/firmware.h>
|
|
#include <linux/tick.h>
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/cpu_pm.h>
|
|
#include <linux/kernel.h>
|
|
#include <linux/module.h>
|
|
|
|
#include <asm/cpuidle.h>
|
|
#include <asm/smp_plat.h>
|
|
#include <asm/suspend.h>
|
|
#include <asm/psci.h>
|
|
|
|
#include "pm.h"
|
|
#include "sleep.h"
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
#define TEGRA114_MAX_STATES 2
|
|
#else
|
|
#define TEGRA114_MAX_STATES 1
|
|
#endif
|
|
|
|
#ifdef CONFIG_PM_SLEEP
|
|
static int tegra114_idle_power_down(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv,
|
|
int index)
|
|
{
|
|
local_fiq_disable();
|
|
|
|
tegra_set_cpu_in_lp2();
|
|
cpu_pm_enter();
|
|
|
|
call_firmware_op(prepare_idle);
|
|
|
|
/* Do suspend by ourselves if the firmware does not implement it */
|
|
if (call_firmware_op(do_idle, 0) == -ENOSYS)
|
|
cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
|
|
|
|
cpu_pm_exit();
|
|
tegra_clear_cpu_in_lp2();
|
|
|
|
local_fiq_enable();
|
|
|
|
return index;
|
|
}
|
|
|
|
static void tegra114_idle_enter_freeze(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv,
|
|
int index)
|
|
{
|
|
tegra114_idle_power_down(dev, drv, index);
|
|
}
|
|
#endif
|
|
|
|
static struct cpuidle_driver tegra_idle_driver = {
|
|
.name = "tegra_idle",
|
|
.owner = THIS_MODULE,
|
|
.state_count = TEGRA114_MAX_STATES,
|
|
.states = {
|
|
[0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
|
|
#ifdef CONFIG_PM_SLEEP
|
|
[1] = {
|
|
.enter = tegra114_idle_power_down,
|
|
.enter_freeze = tegra114_idle_enter_freeze,
|
|
.exit_latency = 500,
|
|
.target_residency = 1000,
|
|
.flags = CPUIDLE_FLAG_TIMER_STOP,
|
|
.power_usage = 0,
|
|
.name = "powered-down",
|
|
.desc = "CPU power gated",
|
|
},
|
|
#endif
|
|
},
|
|
};
|
|
|
|
int __init tegra114_cpuidle_init(void)
|
|
{
|
|
if (!psci_smp_available())
|
|
return cpuidle_register(&tegra_idle_driver, NULL);
|
|
|
|
return 0;
|
|
}
|