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        int ret;
  40
  41        if (!idx) {
  42                cpu_do_idle();
  43                return idx;
  44        }
  45
  46        ret = cpu_pm_enter();
  47        if (!ret) {
  48                /*
  49                 * Pass idle state index to cpu_suspend which in turn will
  50                 * call the CPU ops suspend protocol with idle index as a
  51                 * parameter.
  52                 */
  53                ret = arm_cpuidle_suspend(idx);
  54
  55                cpu_pm_exit();
  56        }
  57
  58        return ret ? -1 : idx;
  59}
  60
  61static struct cpuidle_driver arm_idle_driver = {
  62        .name = "arm_idle",
  63        .owner = THIS_MODULE,
  64        /*
  65         * State at index 0 is standby wfi and considered standard
  66         * on all ARM platforms. If in some platforms simple wfi
  67         * can't be used as "state 0", DT bindings must be implemented
  68         * to work around this issue and allow installing a special
  69         * handler for idle state index 0.
  70         */
  71        .states[0] = {
  72                .enter                  = arm_enter_idle_state,
  73                .exit_latency           = 1,
  74                .target_residency       = 1,
  75                .power_usage            = UINT_MAX,
  76                .name                   = "WFI",
  77                .desc                   = "ARM WFI",
  78        }
  79};
  80
  81static const struct of_device_id arm_idle_state_match[] __initconst = {
  82        { .compatible = "arm,idle-state",
  83          .data = arm_enter_idle_state },
  84        { },
  85};
  86
  87/*
  88 * arm_idle_init
  89 *
  90 * Registers the arm specific cpuidle driver with the cpuidle
  91 * framework. It relies on core code to parse the idle states
  92 * and initialize them using driver data structures accordingly.
  93 */
  94static int __init arm_idle_init(void)
  95{
  96        int cpu, ret;
  97        struct cpuidle_driver *drv = &arm_idle_driver;
  98        struct cpuidle_device *dev;
  99
 100        /*
 101         * Initialize idle states data, starting at index 1.
 102         * This driver is DT only, if no DT idle states are detected (ret == 0)
 103         * let the driver initialization fail accordingly since there is no
 104         * reason to initialize the idle driver if only wfi is supported.
 105         */
 106        ret = dt_init_idle_driver(drv, arm_idle_state_match, 1);
 107        if (ret <= 0)
 108                return ret ? : -ENODEV;
 109
 110        ret = cpuidle_register_driver(drv);
 111        if (ret) {
 112                pr_err("Failed to register cpuidle driver\n");
 113                return ret;
 114        }
 115
 116        /*
 117         * Call arch CPU operations in order to initialize
 118         * idle states suspend back-end specific data
 119         */
 120        for_each_possible_cpu(cpu) {
 121                ret = arm_cpuidle_init(cpu);
 122
 123                /*
 124                 * Skip the cpuidle device initialization if the reported
 125                 * failure is a HW misconfiguration/breakage (-ENXIO).
 126                 */
 127                if (ret == -ENXIO)
 128                        continue;
 129
 130                if (ret) {
 131                        pr_err("CPU %d failed to init idle CPU ops\n", cpu);
 132                        goto out_fail;
 133                }
 134
 135                dev = kzalloc(sizeof(*dev), GFP_KERNEL);
 136                if (!dev) {
 137                        pr_err("Failed to allocate cpuidle device\n");
 138                        goto out_fail;
 139                }
 140                dev->cpu = cpu;
 141
 142                ret = cpuidle_register_device(dev);
 143                if (ret) {
 144                        pr_err("Failed to register cpuidle device for CPU %d\n",
 145                               cpu);
 146                        kfree(dev);
 147                        goto out_fail;
 148                }
 149        }
 150
 151        return 0;
 152out_fail:
 153        while (--cpu >= 0) {
 154                dev = per_cpu(cpuidle_devices, cpu);
 155                cpuidle_unregister_device(dev);
 156                kfree(dev);
 157        }
 158
 159        cpuidle_unregister_driver(drv);
 160
 161        return ret;
 162}
 163device_initcall(arm_idle_init);
 164