2 * ARM64 generic CPU idle driver.
4 * Copyright (C) 2014 ARM Ltd.
5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
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.
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>
20 #include <asm/suspend.h>
22 #include "of_idle_states.h"
24 typedef int (*suspend_init_fn)(struct cpuidle_driver *,
25 struct device_node *[]);
27 struct cpu_suspend_ops {
29 suspend_init_fn init_fn;
32 static const struct cpu_suspend_ops suspend_operations[] __initconst = {
33 {"arm,psci", psci_dt_register_idle_states},
37 static __init const struct cpu_suspend_ops *get_suspend_ops(const char *str)
44 for (i = 0; suspend_operations[i].id; i++)
45 if (!strcmp(suspend_operations[i].id, str))
46 return &suspend_operations[i];
52 * arm_enter_idle_state - Programs CPU to enter the specified state
54 * @dev: cpuidle device
55 * @drv: cpuidle driver
58 * Called from the CPUidle framework to program the device to the
59 * specified target state selected by the governor.
61 static int arm_enter_idle_state(struct cpuidle_device *dev,
62 struct cpuidle_driver *drv, int idx)
73 * Pass idle state index to cpu_suspend which in turn will call
74 * the CPU ops suspend protocol with idle index as a parameter.
76 * Some states would not require context to be saved and flushed
77 * to DRAM, so calling cpu_suspend would not be stricly necessary.
78 * When power domains specifications for ARM CPUs are finalized then
79 * this code can be optimized to prevent saving registers if not
82 ret = cpu_suspend(idx);
86 return ret ? -1 : idx;
89 struct cpuidle_driver arm64_idle_driver = {
94 static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata;
99 * Registers the arm64 specific cpuidle driver with the cpuidle
100 * framework. It relies on core code to parse the idle states
101 * and initialize them using driver data structures accordingly.
103 static int __init arm64_idle_init(void)
106 const char *entry_method;
107 struct device_node *idle_states_node;
108 const struct cpu_suspend_ops *suspend_init;
109 struct cpuidle_driver *drv = &arm64_idle_driver;
111 idle_states_node = of_find_node_by_path("/cpus/idle-states");
112 if (!idle_states_node)
115 if (of_property_read_string(idle_states_node, "entry-method",
117 pr_warn(" * %s missing entry-method property\n",
118 idle_states_node->full_name);
119 of_node_put(idle_states_node);
123 suspend_init = get_suspend_ops(entry_method);
125 pr_warn("Missing suspend initializer\n");
126 of_node_put(idle_states_node);
131 * State at index 0 is standby wfi and considered standard
132 * on all ARM platforms. If in some platforms simple wfi
133 * can't be used as "state 0", DT bindings must be implemented
134 * to work around this issue and allow installing a special
135 * handler for idle state index 0.
137 drv->states[0].exit_latency = 1;
138 drv->states[0].target_residency = 1;
139 drv->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
140 strncpy(drv->states[0].name, "ARM WFI", CPUIDLE_NAME_LEN);
141 strncpy(drv->states[0].desc, "ARM WFI", CPUIDLE_DESC_LEN);
143 drv->cpumask = (struct cpumask *) cpu_possible_mask;
145 * Start at index 1, request idle state nodes to be filled
147 ret = of_init_idle_driver(drv, state_nodes, 1, true);
151 if (suspend_init->init_fn(drv, state_nodes))
154 for (i = 0; i < drv->state_count; i++)
155 drv->states[i].enter = arm_enter_idle_state;
157 return cpuidle_register(drv, NULL);
159 device_initcall(arm64_idle_init);