linux/drivers/cpuidle/cpuidle-arm.c
<<
>>
Prefs
   1/*
   2 * ARM/ARM64 generic CPU idle driver.
   3 *
   4 * Copyright (C) 2014 ARM Ltd.
   5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
   6 *
   7 * This program is free software; you can redistribute it and/or modify
   8 * it under the terms of the GNU General Public License version 2 as
   9 * published by the Free Software Foundation.
  10 */
  11
  12#define pr_fmt(fmt) "CPUidle arm: " fmt
  13
  14#include <linux/cpuidle.h>
  15#include <linux/cpumask.h>
  16#include <linux/cpu_pm.h>
  17#include <linux/kernel.h>
  18#include <linux/module.h>
  19#include <linux/of.h>
  20#include <linux/slab.h>
  21
  22#include <asm/cpuidle.h>
  23
  24#include "dt_idle_states.h"
  25
  26/*
  27 * arm_enter_idle_state - Programs CPU to enter the specified state
  28 *
  29 * dev: cpuidle device
  30 * drv: cpuidle driver
  31 * idx: state index
  32 *
  33 * Called from the CPUidle framework to program the device to the
  34 * specified target state selected by the governor.
  35 */
  36static int arm_enter_idle_state(struct cpuidle_device *dev,
  37                                struct cpuidle_driver *drv, int idx)
  38{
  39        /*
  40         * Pass idle state index to arm_cpuidle_suspend which in turn
  41         * will call the CPU ops suspend protocol with idle index as a
  42         * parameter.
  43         */
  44        return CPU_PM_CPU_IDLE_ENTER(arm_cpuidle_suspend, idx);
  45}
  46
  47static struct cpuidle_driver arm_idle_driver = {
  48        .name = "arm_idle",
  49        .owner = THIS_MODULE,
  50        /*
  51         * State at index 0 is standby wfi and considered standard
  52         * on all ARM platforms. If in some platforms simple wfi
  53         * can't be used as "state 0", DT bindings must be implemented
  54         * to work around this issue and allow installing a special
  55         * handler for idle state index 0.
  56         */
  57        .states[0] = {
  58                .enter                  = arm_enter_idle_state,
  59                .exit_latency           = 1,
  60                .target_residency       = 1,
  61                .power_usage            = UINT_MAX,
  62                .name                   = "WFI",
  63                .desc                   = "ARM WFI",
  64        }
  65};
  66
  67static const struct of_device_id arm_idle_state_match[] __initconst = {
  68        { .compatible = "arm,idle-state",
  69          .data = arm_enter_idle_state },
  70        { },
  71};
  72
  73/*
  74 * arm_idle_init
  75 *
  76 * Registers the arm specific cpuidle driver with the cpuidle
  77 * framework. It relies on core code to parse the idle states
  78 * and initialize them using driver data structures accordingly.
  79 */
  80static int __init arm_idle_init(void)
  81{
  82        int cpu, ret;
  83        struct cpuidle_driver *drv = &arm_idle_driver;
  84        struct cpuidle_device *dev;
  85
  86        /*
  87         * Initialize idle states data, starting at index 1.
  88         * This driver is DT only, if no DT idle states are detected (ret == 0)
  89         * let the driver initialization fail accordingly since there is no
  90         * reason to initialize the idle driver if only wfi is supported.
  91         */
  92        ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
  93        if (ret <= 0)
  94                return ret ? : -ENODEV;
  95
  96        ret = cpuidle_register_driver(drv);
  97        if (ret) {
  98                pr_err("Failed to register cpuidle driver\n");
  99                return ret;
 100        }
 101
 102        /*
 103         * Call arch CPU operations in order to initialize
 104         * idle states suspend back-end specific data
 105         */
 106        for_each_possible_cpu(cpu) {
 107                ret = arm_cpuidle_init(cpu);
 108
 109                /*
 110                 * Skip the cpuidle device initialization if the reported
 111                 * failure is a HW misconfiguration/breakage (-ENXIO).
 112                 */
 113                if (ret == -ENXIO)
 114                        continue;
 115
 116                if (ret) {
 117                        pr_err("CPU %d failed to init idle CPU ops\n", cpu);
 118                        goto out_fail;
 119                }
 120
 121                dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 122                if (!dev) {
 123                        pr_err("Failed to allocate cpuidle device\n");
 124                        ret = -ENOMEM;
 125                        goto out_fail;
 126                }
 127                dev->cpu = cpu;
 128
 129                ret = cpuidle_register_device(dev);
 130                if (ret) {
 131                        pr_err("Failed to register cpuidle device for CPU %d\n",
 132                               cpu);
 133                        kfree(dev);
 134                        goto out_fail;
 135                }
 136        }
 137
 138        return 0;
 139out_fail:
 140        while (--cpu >= 0) {
 141                dev = per_cpu(cpuidle_devices, cpu);
 142                cpuidle_unregister_device(dev);
 143                kfree(dev);
 144        }
 145
 146        cpuidle_unregister_driver(drv);
 147
 148        return ret;
 149}
 150device_initcall(arm_idle_init);
 151