linux/drivers/cpuidle/cpuidle-pseries.c
<<
>>
Prefs
   1/*
   2 *  cpuidle-pseries - idle state cpuidle driver.
   3 *  Adapted from drivers/idle/intel_idle.c and
   4 *  drivers/acpi/processor_idle.c
   5 *
   6 */
   7
   8#include <linux/kernel.h>
   9#include <linux/module.h>
  10#include <linux/init.h>
  11#include <linux/moduleparam.h>
  12#include <linux/cpuidle.h>
  13#include <linux/cpu.h>
  14#include <linux/notifier.h>
  15
  16#include <asm/paca.h>
  17#include <asm/reg.h>
  18#include <asm/machdep.h>
  19#include <asm/firmware.h>
  20#include <asm/runlatch.h>
  21#include <asm/plpar_wrappers.h>
  22
  23struct cpuidle_driver pseries_idle_driver = {
  24        .name             = "pseries_idle",
  25        .owner            = THIS_MODULE,
  26};
  27
  28static int max_idle_state;
  29static struct cpuidle_state *cpuidle_state_table;
  30static u64 snooze_timeout;
  31static bool snooze_timeout_en;
  32
  33static inline void idle_loop_prolog(unsigned long *in_purr)
  34{
  35        ppc64_runlatch_off();
  36        *in_purr = mfspr(SPRN_PURR);
  37        /*
  38         * Indicate to the HV that we are idle. Now would be
  39         * a good time to find other work to dispatch.
  40         */
  41        get_lppaca()->idle = 1;
  42}
  43
  44static inline void idle_loop_epilog(unsigned long in_purr)
  45{
  46        u64 wait_cycles;
  47
  48        wait_cycles = be64_to_cpu(get_lppaca()->wait_state_cycles);
  49        wait_cycles += mfspr(SPRN_PURR) - in_purr;
  50        get_lppaca()->wait_state_cycles = cpu_to_be64(wait_cycles);
  51        get_lppaca()->idle = 0;
  52
  53        if (irqs_disabled())
  54                local_irq_enable();
  55        ppc64_runlatch_on();
  56}
  57
  58static int snooze_loop(struct cpuidle_device *dev,
  59                        struct cpuidle_driver *drv,
  60                        int index)
  61{
  62        unsigned long in_purr;
  63        u64 snooze_exit_time;
  64
  65        idle_loop_prolog(&in_purr);
  66        local_irq_enable();
  67        set_thread_flag(TIF_POLLING_NRFLAG);
  68        snooze_exit_time = get_tb() + snooze_timeout;
  69
  70        while (!need_resched()) {
  71                HMT_low();
  72                HMT_very_low();
  73                if (snooze_timeout_en && get_tb() > snooze_exit_time)
  74                        break;
  75        }
  76
  77        HMT_medium();
  78        clear_thread_flag(TIF_POLLING_NRFLAG);
  79        smp_mb();
  80
  81        idle_loop_epilog(in_purr);
  82
  83        return index;
  84}
  85
  86static void check_and_cede_processor(void)
  87{
  88        /*
  89         * Ensure our interrupt state is properly tracked,
  90         * also checks if no interrupt has occurred while we
  91         * were soft-disabled
  92         */
  93        if (prep_irq_for_idle()) {
  94                cede_processor();
  95#ifdef CONFIG_TRACE_IRQFLAGS
  96                /* Ensure that H_CEDE returns with IRQs on */
  97                if (WARN_ON(!(mfmsr() & MSR_EE)))
  98                        __hard_irq_enable();
  99#endif
 100        }
 101}
 102
 103static int dedicated_cede_loop(struct cpuidle_device *dev,
 104                                struct cpuidle_driver *drv,
 105                                int index)
 106{
 107        unsigned long in_purr;
 108
 109        idle_loop_prolog(&in_purr);
 110        get_lppaca()->donate_dedicated_cpu = 1;
 111
 112        HMT_medium();
 113        check_and_cede_processor();
 114
 115        get_lppaca()->donate_dedicated_cpu = 0;
 116
 117        idle_loop_epilog(in_purr);
 118
 119        return index;
 120}
 121
 122static int shared_cede_loop(struct cpuidle_device *dev,
 123                        struct cpuidle_driver *drv,
 124                        int index)
 125{
 126        unsigned long in_purr;
 127
 128        idle_loop_prolog(&in_purr);
 129
 130        /*
 131         * Yield the processor to the hypervisor.  We return if
 132         * an external interrupt occurs (which are driven prior
 133         * to returning here) or if a prod occurs from another
 134         * processor. When returning here, external interrupts
 135         * are enabled.
 136         */
 137        check_and_cede_processor();
 138
 139        idle_loop_epilog(in_purr);
 140
 141        return index;
 142}
 143
 144/*
 145 * States for dedicated partition case.
 146 */
 147static struct cpuidle_state dedicated_states[] = {
 148        { /* Snooze */
 149                .name = "snooze",
 150                .desc = "snooze",
 151                .exit_latency = 0,
 152                .target_residency = 0,
 153                .enter = &snooze_loop },
 154        { /* CEDE */
 155                .name = "CEDE",
 156                .desc = "CEDE",
 157                .exit_latency = 10,
 158                .target_residency = 100,
 159                .enter = &dedicated_cede_loop },
 160};
 161
 162/*
 163 * States for shared partition case.
 164 */
 165static struct cpuidle_state shared_states[] = {
 166        { /* Shared Cede */
 167                .name = "Shared Cede",
 168                .desc = "Shared Cede",
 169                .exit_latency = 0,
 170                .target_residency = 0,
 171                .enter = &shared_cede_loop },
 172};
 173
 174static int pseries_cpuidle_add_cpu_notifier(struct notifier_block *n,
 175                        unsigned long action, void *hcpu)
 176{
 177        int hotcpu = (unsigned long)hcpu;
 178        struct cpuidle_device *dev =
 179                                per_cpu(cpuidle_devices, hotcpu);
 180
 181        if (dev && cpuidle_get_driver()) {
 182                switch (action) {
 183                case CPU_ONLINE:
 184                case CPU_ONLINE_FROZEN:
 185                        cpuidle_pause_and_lock();
 186                        cpuidle_enable_device(dev);
 187                        cpuidle_resume_and_unlock();
 188                        break;
 189
 190                case CPU_DEAD:
 191                case CPU_DEAD_FROZEN:
 192                        cpuidle_pause_and_lock();
 193                        cpuidle_disable_device(dev);
 194                        cpuidle_resume_and_unlock();
 195                        break;
 196
 197                default:
 198                        return NOTIFY_DONE;
 199                }
 200        }
 201        return NOTIFY_OK;
 202}
 203
 204static struct notifier_block setup_hotplug_notifier = {
 205        .notifier_call = pseries_cpuidle_add_cpu_notifier,
 206};
 207
 208/*
 209 * pseries_cpuidle_driver_init()
 210 */
 211static int pseries_cpuidle_driver_init(void)
 212{
 213        int idle_state;
 214        struct cpuidle_driver *drv = &pseries_idle_driver;
 215
 216        drv->state_count = 0;
 217
 218        for (idle_state = 0; idle_state < max_idle_state; ++idle_state) {
 219                /* Is the state not enabled? */
 220                if (cpuidle_state_table[idle_state].enter == NULL)
 221                        continue;
 222
 223                drv->states[drv->state_count] = /* structure copy */
 224                        cpuidle_state_table[idle_state];
 225
 226                drv->state_count += 1;
 227        }
 228
 229        return 0;
 230}
 231
 232/*
 233 * pseries_idle_probe()
 234 * Choose state table for shared versus dedicated partition
 235 */
 236static int pseries_idle_probe(void)
 237{
 238
 239        if (cpuidle_disable != IDLE_NO_OVERRIDE)
 240                return -ENODEV;
 241
 242        if (firmware_has_feature(FW_FEATURE_SPLPAR)) {
 243                if (lppaca_shared_proc(get_lppaca())) {
 244                        cpuidle_state_table = shared_states;
 245                        max_idle_state = ARRAY_SIZE(shared_states);
 246                } else {
 247                        cpuidle_state_table = dedicated_states;
 248                        max_idle_state = ARRAY_SIZE(dedicated_states);
 249                }
 250        } else
 251                return -ENODEV;
 252
 253        if (max_idle_state > 1) {
 254                snooze_timeout_en = true;
 255                snooze_timeout = cpuidle_state_table[1].target_residency *
 256                                 tb_ticks_per_usec;
 257        }
 258        return 0;
 259}
 260
 261static int __init pseries_processor_idle_init(void)
 262{
 263        int retval;
 264
 265        retval = pseries_idle_probe();
 266        if (retval)
 267                return retval;
 268
 269        pseries_cpuidle_driver_init();
 270        retval = cpuidle_register(&pseries_idle_driver, NULL);
 271        if (retval) {
 272                printk(KERN_DEBUG "Registration of pseries driver failed.\n");
 273                return retval;
 274        }
 275
 276        register_cpu_notifier(&setup_hotplug_notifier);
 277        printk(KERN_DEBUG "pseries_idle_driver registered\n");
 278        return 0;
 279}
 280
 281device_initcall(pseries_processor_idle_init);
 282