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