linux/arch/arm/mach-tegra/cpuidle-tegra30.c
<<
>>
Prefs
   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/kernel.h>
  23#include <linux/module.h>
  24#include <linux/cpuidle.h>
  25#include <linux/cpu_pm.h>
  26#include <linux/clockchips.h>
  27#include <linux/clk/tegra.h>
  28
  29#include <asm/cpuidle.h>
  30#include <asm/proc-fns.h>
  31#include <asm/suspend.h>
  32#include <asm/smp_plat.h>
  33
  34#include "pm.h"
  35#include "sleep.h"
  36
  37#ifdef CONFIG_PM_SLEEP
  38static int tegra30_idle_lp2(struct cpuidle_device *dev,
  39                            struct cpuidle_driver *drv,
  40                            int index);
  41#endif
  42
  43static struct cpuidle_driver tegra_idle_driver = {
  44        .name = "tegra_idle",
  45        .owner = THIS_MODULE,
  46#ifdef CONFIG_PM_SLEEP
  47        .state_count = 2,
  48#else
  49        .state_count = 1,
  50#endif
  51        .states = {
  52                [0] = ARM_CPUIDLE_WFI_STATE_PWR(600),
  53#ifdef CONFIG_PM_SLEEP
  54                [1] = {
  55                        .enter                  = tegra30_idle_lp2,
  56                        .exit_latency           = 2000,
  57                        .target_residency       = 2200,
  58                        .power_usage            = 0,
  59                        .flags                  = CPUIDLE_FLAG_TIME_VALID,
  60                        .name                   = "powered-down",
  61                        .desc                   = "CPU power gated",
  62                },
  63#endif
  64        },
  65};
  66
  67#ifdef CONFIG_PM_SLEEP
  68static bool tegra30_cpu_cluster_power_down(struct cpuidle_device *dev,
  69                                           struct cpuidle_driver *drv,
  70                                           int index)
  71{
  72        /* All CPUs entering LP2 is not working.
  73         * Don't let CPU0 enter LP2 when any secondary CPU is online.
  74         */
  75        if (num_online_cpus() > 1 || !tegra_cpu_rail_off_ready()) {
  76                cpu_do_idle();
  77                return false;
  78        }
  79
  80        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
  81
  82        tegra_idle_lp2_last();
  83
  84        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
  85
  86        return true;
  87}
  88
  89#ifdef CONFIG_SMP
  90static bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
  91                                        struct cpuidle_driver *drv,
  92                                        int index)
  93{
  94        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_ENTER, &dev->cpu);
  95
  96        smp_wmb();
  97
  98        cpu_suspend(0, tegra30_sleep_cpu_secondary_finish);
  99
 100        clockevents_notify(CLOCK_EVT_NOTIFY_BROADCAST_EXIT, &dev->cpu);
 101
 102        return true;
 103}
 104#else
 105static inline bool tegra30_cpu_core_power_down(struct cpuidle_device *dev,
 106                                               struct cpuidle_driver *drv,
 107                                               int index)
 108{
 109        return true;
 110}
 111#endif
 112
 113static int tegra30_idle_lp2(struct cpuidle_device *dev,
 114                            struct cpuidle_driver *drv,
 115                            int index)
 116{
 117        u32 cpu = is_smp() ? cpu_logical_map(dev->cpu) : dev->cpu;
 118        bool entered_lp2 = false;
 119        bool last_cpu;
 120
 121        local_fiq_disable();
 122
 123        last_cpu = tegra_set_cpu_in_lp2(cpu);
 124        cpu_pm_enter();
 125
 126        if (cpu == 0) {
 127                if (last_cpu)
 128                        entered_lp2 = tegra30_cpu_cluster_power_down(dev, drv,
 129                                                                     index);
 130                else
 131                        cpu_do_idle();
 132        } else {
 133                entered_lp2 = tegra30_cpu_core_power_down(dev, drv, index);
 134        }
 135
 136        cpu_pm_exit();
 137        tegra_clear_cpu_in_lp2(cpu);
 138
 139        local_fiq_enable();
 140
 141        smp_rmb();
 142
 143        return (entered_lp2) ? index : 0;
 144}
 145#endif
 146
 147int __init tegra30_cpuidle_init(void)
 148{
 149#ifdef CONFIG_PM_SLEEP
 150        tegra_tear_down_cpu = tegra30_tear_down_cpu;
 151#endif
 152        return cpuidle_register(&tegra_idle_driver, NULL);
 153}
 154