Loading...
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 | // 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 <asm/cpuidle.h> #include <asm/smp_plat.h> #include <asm/suspend.h> #include "cpuidle.h" #include "pm.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; } tegra_idle_lp2_last(); return true; } #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_sleep_cpu_secondary_finish); 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; bool last_cpu; local_fiq_disable(); last_cpu = tegra_set_cpu_in_lp2(); cpu_pm_enter(); if (dev->cpu == 0) { if (last_cpu) entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv, index); else cpu_do_idle(); } else { entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index); } cpu_pm_exit(); tegra_clear_cpu_in_lp2(); local_fiq_enable(); smp_rmb(); return (entered_lp2) ? index : 0; } #endif int __init tegra30_cpuidle_init(void) { return cpuidle_register(&tegra_idle_driver, NULL); } |