Linux Audio

Check our new training course

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