~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

TOMOYO Linux Cross Reference
Linux/arch/arm/mach-tegra/cpuidle-tegra20.c

Version: ~ [ linux-5.18 ] ~ [ linux-5.17.9 ] ~ [ linux-5.16.20 ] ~ [ linux-5.15.41 ] ~ [ linux-5.14.21 ] ~ [ linux-5.13.19 ] ~ [ linux-5.12.19 ] ~ [ linux-5.11.22 ] ~ [ linux-5.10.117 ] ~ [ linux-5.9.16 ] ~ [ linux-5.8.18 ] ~ [ linux-5.7.19 ] ~ [ linux-5.6.19 ] ~ [ linux-5.5.19 ] ~ [ linux-5.4.195 ] ~ [ linux-5.3.18 ] ~ [ linux-5.2.21 ] ~ [ linux-5.1.21 ] ~ [ linux-5.0.21 ] ~ [ linux-4.20.17 ] ~ [ linux-4.19.244 ] ~ [ linux-4.18.20 ] ~ [ linux-4.17.19 ] ~ [ linux-4.16.18 ] ~ [ linux-4.15.18 ] ~ [ linux-4.14.280 ] ~ [ linux-4.13.16 ] ~ [ linux-4.12.14 ] ~ [ linux-4.11.12 ] ~ [ linux-4.10.17 ] ~ [ linux-4.9.315 ] ~ [ linux-4.8.17 ] ~ [ linux-4.7.10 ] ~ [ linux-4.6.7 ] ~ [ linux-4.5.7 ] ~ [ linux-4.4.302 ] ~ [ linux-4.3.6 ] ~ [ linux-4.2.8 ] ~ [ linux-4.1.52 ] ~ [ linux-4.0.9 ] ~ [ linux-3.10.108 ] ~ [ linux-2.6.32.71 ] ~ [ linux-2.6.0 ] ~ [ linux-2.4.37.11 ] ~ [ unix-v6-master ] ~ [ ccs-tools-1.8.9 ] ~ [ policy-sample ] ~
Architecture: ~ [ i386 ] ~ [ alpha ] ~ [ m68k ] ~ [ mips ] ~ [ ppc ] ~ [ sparc ] ~ [ sparc64 ] ~

  1 /*
  2  * CPU idle driver for Tegra CPUs
  3  *
  4  * Copyright (c) 2010-2012, NVIDIA Corporation.
  5  * Copyright (c) 2011 Google, Inc.
  6  * Author: Colin Cross <ccross@android.com>
  7  *         Gary King <gking@nvidia.com>
  8  *
  9  * Rework for 3.3 by Peter De Schrijver <pdeschrijver@nvidia.com>
 10  *
 11  * This program is free software; you can redistribute it and/or modify
 12  * it under the terms of the GNU General Public License as published by
 13  * the Free Software Foundation; either version 2 of the License, or
 14  * (at your option) any later version.
 15  *
 16  * This program is distributed in the hope that it will be useful, but WITHOUT
 17  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 18  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
 19  * more details.
 20  */
 21 
 22 #include <linux/clk/tegra.h>
 23 #include <linux/tick.h>
 24 #include <linux/cpuidle.h>
 25 #include <linux/cpu_pm.h>
 26 #include <linux/kernel.h>
 27 #include <linux/module.h>
 28 
 29 #include <soc/tegra/flowctrl.h>
 30 
 31 #include <asm/cpuidle.h>
 32 #include <asm/smp_plat.h>
 33 #include <asm/suspend.h>
 34 
 35 #include "cpuidle.h"
 36 #include "iomap.h"
 37 #include "irq.h"
 38 #include "pm.h"
 39 #include "reset.h"
 40 #include "sleep.h"
 41 
 42 #ifdef CONFIG_PM_SLEEP
 43 static bool abort_flag;
 44 static atomic_t abort_barrier;
 45 static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
 46                                     struct cpuidle_driver *drv,
 47                                     int index);
 48 #define TEGRA20_MAX_STATES 2
 49 #else
 50 #define TEGRA20_MAX_STATES 1
 51 #endif
 52 
 53 static struct cpuidle_driver tegra_idle_driver = {
 54         .name = "tegra_idle",
 55         .owner = THIS_MODULE,
 56         .states = {
 57                 ARM_CPUIDLE_WFI_STATE_PWR(600),
 58 #ifdef CONFIG_PM_SLEEP
 59                 {
 60                         .enter            = tegra20_idle_lp2_coupled,
 61                         .exit_latency     = 5000,
 62                         .target_residency = 10000,
 63                         .power_usage      = 0,
 64                         .flags            = CPUIDLE_FLAG_COUPLED,
 65                         .name             = "powered-down",
 66                         .desc             = "CPU power gated",
 67                 },
 68 #endif
 69         },
 70         .state_count = TEGRA20_MAX_STATES,
 71         .safe_state_index = 0,
 72 };
 73 
 74 #ifdef CONFIG_PM_SLEEP
 75 #ifdef CONFIG_SMP
 76 static int tegra20_reset_sleeping_cpu_1(void)
 77 {
 78         int ret = 0;
 79 
 80         tegra_pen_lock();
 81 
 82         if (readb(tegra20_cpu1_resettable_status) == CPU_RESETTABLE)
 83                 tegra20_cpu_shutdown(1);
 84         else
 85                 ret = -EINVAL;
 86 
 87         tegra_pen_unlock();
 88 
 89         return ret;
 90 }
 91 
 92 static void tegra20_wake_cpu1_from_reset(void)
 93 {
 94         tegra_pen_lock();
 95 
 96         tegra20_cpu_clear_resettable();
 97 
 98         /* enable cpu clock on cpu */
 99         tegra_enable_cpu_clock(1);
100 
101         /* take the CPU out of reset */
102         tegra_cpu_out_of_reset(1);
103 
104         /* unhalt the cpu */
105         flowctrl_write_cpu_halt(1, 0);
106 
107         tegra_pen_unlock();
108 }
109 
110 static int tegra20_reset_cpu_1(void)
111 {
112         if (!cpu_online(1) || !tegra20_reset_sleeping_cpu_1())
113                 return 0;
114 
115         tegra20_wake_cpu1_from_reset();
116         return -EBUSY;
117 }
118 #else
119 static inline void tegra20_wake_cpu1_from_reset(void)
120 {
121 }
122 
123 static inline int tegra20_reset_cpu_1(void)
124 {
125         return 0;
126 }
127 #endif
128 
129 static bool tegra20_cpu_cluster_power_down(struct cpuidle_device *dev,
130                                            struct cpuidle_driver *drv,
131                                            int index)
132 {
133         while (tegra20_cpu_is_resettable_soon())
134                 cpu_relax();
135 
136         if (tegra20_reset_cpu_1() || !tegra_cpu_rail_off_ready())
137                 return false;
138 
139         tick_broadcast_enter();
140 
141         tegra_idle_lp2_last();
142 
143         tick_broadcast_exit();
144 
145         if (cpu_online(1))
146                 tegra20_wake_cpu1_from_reset();
147 
148         return true;
149 }
150 
151 #ifdef CONFIG_SMP
152 static bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
153                                          struct cpuidle_driver *drv,
154                                          int index)
155 {
156         tick_broadcast_enter();
157 
158         cpu_suspend(0, tegra20_sleep_cpu_secondary_finish);
159 
160         tegra20_cpu_clear_resettable();
161 
162         tick_broadcast_exit();
163 
164         return true;
165 }
166 #else
167 static inline bool tegra20_idle_enter_lp2_cpu_1(struct cpuidle_device *dev,
168                                                 struct cpuidle_driver *drv,
169                                                 int index)
170 {
171         return true;
172 }
173 #endif
174 
175 static int tegra20_idle_lp2_coupled(struct cpuidle_device *dev,
176                                     struct cpuidle_driver *drv,
177                                     int index)
178 {
179         bool entered_lp2 = false;
180 
181         if (tegra_pending_sgi())
182                 WRITE_ONCE(abort_flag, true);
183 
184         cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
185 
186         if (abort_flag) {
187                 cpuidle_coupled_parallel_barrier(dev, &abort_barrier);
188                 abort_flag = false;     /* clean flag for next coming */
189                 return -EINTR;
190         }
191 
192         local_fiq_disable();
193 
194         tegra_set_cpu_in_lp2();
195         cpu_pm_enter();
196 
197         if (dev->cpu == 0)
198                 entered_lp2 = tegra20_cpu_cluster_power_down(dev, drv, index);
199         else
200                 entered_lp2 = tegra20_idle_enter_lp2_cpu_1(dev, drv, index);
201 
202         cpu_pm_exit();
203         tegra_clear_cpu_in_lp2();
204 
205         local_fiq_enable();
206 
207         smp_rmb();
208 
209         return entered_lp2 ? index : 0;
210 }
211 #endif
212 
213 /*
214  * Tegra20 HW appears to have a bug such that PCIe device interrupts, whether
215  * they are legacy IRQs or MSI, are lost when LP2 is enabled. To work around
216  * this, simply disable LP2 if the PCI driver and DT node are both enabled.
217  */
218 void tegra20_cpuidle_pcie_irqs_in_use(void)
219 {
220         pr_info_once(
221                 "Disabling cpuidle LP2 state, since PCIe IRQs are in use\n");
222         tegra_idle_driver.states[1].disabled = true;
223 }
224 
225 int __init tegra20_cpuidle_init(void)
226 {
227         return cpuidle_register(&tegra_idle_driver, cpu_possible_mask);
228 }
229 

~ [ source navigation ] ~ [ diff markup ] ~ [ identifier search ] ~

kernel.org | git.kernel.org | LWN.net | Project Home | Wiki (Japanese) | Wiki (English) | SVN repository | Mail admin

Linux® is a registered trademark of Linus Torvalds in the United States and other countries.
TOMOYO® is a registered trademark of NTT DATA CORPORATION.

osdn.jp