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