blob: 2447427cb4a8f2e9510ec4fe60944a52b2fd324c [file] [log] [blame]
Thomas Gleixnerc942fdd2019-05-27 08:55:06 +02001// SPDX-License-Identifier: GPL-2.0-or-later
Joseph Lo0b25e252012-10-31 17:41:15 +08002/*
3 * CPU idle driver for Tegra CPUs
4 *
5 * Copyright (c) 2010-2012, NVIDIA Corporation.
6 * Copyright (c) 2011 Google, Inc.
7 * Author: Colin Cross <ccross@android.com>
8 * Gary King <gking@nvidia.com>
9 *
10 * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
Joseph Lo0b25e252012-10-31 17:41:15 +080011 */
12
Thierry Redinga0524ac2014-07-11 09:44:49 +020013#include <linux/clk/tegra.h>
Thomas Gleixnera0b41222015-04-03 02:32:14 +020014#include <linux/tick.h>
Joseph Lo0b25e252012-10-31 17:41:15 +080015#include <linux/cpuidle.h>
Joseph Lo5c1350b2013-01-15 22:10:38 +000016#include <linux/cpu_pm.h>
Thierry Redinga0524ac2014-07-11 09:44:49 +020017#include <linux/kernel.h>
18#include <linux/module.h>
Joseph Lo0b25e252012-10-31 17:41:15 +080019
Jon Hunter7e10cf72017-03-28 13:42:54 +010020#include <soc/tegra/flowctrl.h>
21
Joseph Lo0b25e252012-10-31 17:41:15 +080022#include <asm/cpuidle.h>
Joseph Lo5c1350b2013-01-15 22:10:38 +000023#include <asm/smp_plat.h>
Thierry Redinga0524ac2014-07-11 09:44:49 +020024#include <asm/suspend.h>
Joseph Lo5c1350b2013-01-15 22:10:38 +000025
Thierry Reding755c47e2016-04-28 14:52:45 +020026#include "cpuidle.h"
Joseph Lo1d328602013-01-16 17:33:55 +000027#include "iomap.h"
28#include "irq.h"
Thierry Redinga0524ac2014-07-11 09:44:49 +020029#include "pm.h"
Dmitry Osipenko4d48edb2015-01-15 13:58:57 +030030#include "reset.h"
Thierry Redinga0524ac2014-07-11 09:44:49 +020031#include "sleep.h"
Joseph Lo5c1350b2013-01-15 22:10:38 +000032
33#ifdef CONFIG_PM_SLEEP
Joseph Lo1d328602013-01-16 17:33:55 +000034static bool abort_flag;
35static atomic_t abort_barrier;
36static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
37 struct cpuidle_driver *drv,
38 int index);
Daniel Lezcano14ad7a12013-04-03 12:15:17 +000039#define TEGRA20_MAX_STATES 2
40#else
41#define TEGRA20_MAX_STATES 1
Joseph Lo5c1350b2013-01-15 22:10:38 +000042#endif
43
Joseph Lo0b25e252012-10-31 17:41:15 +080044static struct cpuidle_driver tegra_idle_driver = {
45 .name = "tegra_idle",
46 .owner = THIS_MODULE,
Daniel Lezcano14ad7a12013-04-03 12:15:17 +000047 .states = {
48 ARM_CPUIDLE_WFI_STATE_PWR(600),
49#ifdef CONFIG_PM_SLEEP
50 {
51 .enter = tegra20_idle_lp2_coupled,
52 .exit_latency = 5000,
53 .target_residency = 10000,
54 .power_usage = 0,
Dmitry Osipenko36841ba2019-02-24 18:21:14 +030055 .flags = CPUIDLE_FLAG_COUPLED |
56 CPUIDLE_FLAG_TIMER_STOP,
Daniel Lezcano14ad7a12013-04-03 12:15:17 +000057 .name = "powered-down",
58 .desc = "CPU power gated",
59 },
60#endif
61 },
62 .state_count = TEGRA20_MAX_STATES,
63 .safe_state_index = 0,
Joseph Lo0b25e252012-10-31 17:41:15 +080064};
65
Joseph Lo5c1350b2013-01-15 22:10:38 +000066#ifdef CONFIG_PM_SLEEP
67#ifdef CONFIG_SMP
Joseph Lo1d328602013-01-16 17:33:55 +000068static int tegra20_reset_sleeping_cpu_1(void)
69{
70 int ret = 0;
71
72 tegra_pen_lock();
73
Dmitry Osipenko4d48edb2015-01-15 13:58:57 +030074 if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
Joseph Lo1d328602013-01-16 17:33:55 +000075 tegra20_cpu_shutdown(1);
76 else
77 ret = -EINVAL;
78
79 tegra_pen_unlock();
80
81 return ret;
82}
83
84static void tegra20_wake_cpu1_from_reset(void)
85{
86 tegra_pen_lock();
87
88 tegra20_cpu_clear_resettable();
89
90 /* enable cpu clock on cpu */
91 tegra_enable_cpu_clock(1);
92
93 /* take the CPU out of reset */
94 tegra_cpu_out_of_reset(1);
95
96 /* unhalt the cpu */
97 flowctrl_write_cpu_halt(1, 0);
98
99 tegra_pen_unlock();
100}
101
102static int tegra20_reset_cpu_1(void)
103{
104 if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
105 return 0;
106
107 tegra20_wake_cpu1_from_reset();
108 return -EBUSY;
109}
110#else
111static inline void tegra20_wake_cpu1_from_reset(void)
112{
113}
114
115static inline int tegra20_reset_cpu_1(void)
116{
117 return 0;
118}
119#endif
120
121static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
122 struct cpuidle_driver *drv,
123 int index)
124{
Joseph Lo1d328602013-01-16 17:33:55 +0000125 while (tegra20_cpu_is_resettable_soon())
126 cpu_relax();
127
128 if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
129 return false;
130
Joseph Lo4d82d052013-04-02 01:20:50 +0000131 tegra_idle_lp2_last();
Joseph Lo1d328602013-01-16 17:33:55 +0000132
Joseph Lo1d328602013-01-16 17:33:55 +0000133 if (cpu_online(1))
134 tegra20_wake_cpu1_from_reset();
135
136 return true;
137}
138
139#ifdef CONFIG_SMP
Joseph Lo5c1350b2013-01-15 22:10:38 +0000140static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
141 struct cpuidle_driver *drv,
142 int index)
143{
Joseph Lo5c1350b2013-01-15 22:10:38 +0000144 cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
145
146 tegra20_cpu_clear_resettable();
147
Joseph Lo5c1350b2013-01-15 22:10:38 +0000148 return true;
149}
150#else
151static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
152 struct cpuidle_driver *drv,
153 int index)
154{
155 return true;
156}
157#endif
158
Joseph Lo1d328602013-01-16 17:33:55 +0000159static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
160 struct cpuidle_driver *drv,
161 int index)
Joseph Lo5c1350b2013-01-15 22:10:38 +0000162{
Joseph Lo5c1350b2013-01-15 22:10:38 +0000163 bool entered_lp2 = false;
164
Joseph Lo1d328602013-01-16 17:33:55 +0000165 if (tegra_pending_sgi())
Mark Rutland6aa7de02017-10-23 14:07:29 -0700166 WRITE_ONCE(abort_flag, true);
Joseph Lo1d328602013-01-16 17:33:55 +0000167
168 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
169
170 if (abort_flag) {
171 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
172 abort_flag = false; /* clean flag for next coming */
173 return -EINTR;
174 }
175
Joseph Lo5c1350b2013-01-15 22:10:38 +0000176 local_fiq_disable();
177
Joseph Lo8f6a0b62013-06-04 18:47:35 +0800178 tegra_set_cpu_in_lp2();
Joseph Lo5c1350b2013-01-15 22:10:38 +0000179 cpu_pm_enter();
180
Joseph Lo8f6a0b62013-06-04 18:47:35 +0800181 if (dev->cpu == 0)
Joseph Lo1d328602013-01-16 17:33:55 +0000182 entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
Joseph Lo5c1350b2013-01-15 22:10:38 +0000183 else
184 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
185
186 cpu_pm_exit();
Joseph Lo8f6a0b62013-06-04 18:47:35 +0800187 tegra_clear_cpu_in_lp2();
Joseph Lo5c1350b2013-01-15 22:10:38 +0000188
189 local_fiq_enable();
190
191 smp_rmb();
192
193 return entered_lp2 ? index : 0;
194}
195#endif
196
Stephen Warrenb4f17372013-05-06 14:19:19 -0600197/*
198 * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether
199 * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around
200 * this, simply disable LP2 if the PCI driver and DT node are both enabled.
201 */
202void tegra20_cpuidle_pcie_irqs_in_use(void)
203{
204 pr_info_once(
205 "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n");
206 tegra_idle_driver.states[1].disabled = true;
207}
208
Joseph Lo0b25e252012-10-31 17:41:15 +0800209int __init tegra20_cpuidle_init(void)
210{
Daniel Lezcanoc5106c92013-04-23 08:54:40 +0000211 return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
Joseph Lo0b25e252012-10-31 17:41:15 +0800212}