mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-12-18 00:24:58 +08:00
fcaf20360a
Based on 1 normalized pattern(s): the code contained herein is licensed under the gnu general public license you may obtain a copy of the gnu general public license version 2 or later at the following locations http www opensource org licenses gpl license html http www gnu org copyleft gpl html extracted by the scancode license scanner the SPDX license identifier GPL-2.0-or-later has been chosen to replace the boilerplate/reference in 161 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Richard Fontana <rfontana@redhat.com> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190527070033.383790741@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
150 lines
4.0 KiB
C
150 lines
4.0 KiB
C
// SPDX-License-Identifier: GPL-2.0-or-later
|
|
/*
|
|
* Copyright 2012 Linaro Ltd.
|
|
*/
|
|
|
|
#include <linux/cpuidle.h>
|
|
#include <linux/of.h>
|
|
#include <linux/of_device.h>
|
|
#include <asm/cpuidle.h>
|
|
|
|
extern struct of_cpuidle_method __cpuidle_method_of_table[];
|
|
|
|
static const struct of_cpuidle_method __cpuidle_method_of_table_sentinel
|
|
__used __section(__cpuidle_method_of_table_end);
|
|
|
|
static struct cpuidle_ops cpuidle_ops[NR_CPUS] __ro_after_init;
|
|
|
|
/**
|
|
* arm_cpuidle_simple_enter() - a wrapper to cpu_do_idle()
|
|
* @dev: not used
|
|
* @drv: not used
|
|
* @index: not used
|
|
*
|
|
* A trivial wrapper to allow the cpu_do_idle function to be assigned as a
|
|
* cpuidle callback by matching the function signature.
|
|
*
|
|
* Returns the index passed as parameter
|
|
*/
|
|
int arm_cpuidle_simple_enter(struct cpuidle_device *dev,
|
|
struct cpuidle_driver *drv, int index)
|
|
{
|
|
cpu_do_idle();
|
|
|
|
return index;
|
|
}
|
|
|
|
/**
|
|
* arm_cpuidle_suspend() - function to enter low power idle states
|
|
* @index: an integer used as an identifier for the low level PM callbacks
|
|
*
|
|
* This function calls the underlying arch specific low level PM code as
|
|
* registered at the init time.
|
|
*
|
|
* Returns the result of the suspend callback.
|
|
*/
|
|
int arm_cpuidle_suspend(int index)
|
|
{
|
|
int cpu = smp_processor_id();
|
|
|
|
return cpuidle_ops[cpu].suspend(index);
|
|
}
|
|
|
|
/**
|
|
* arm_cpuidle_get_ops() - find a registered cpuidle_ops by name
|
|
* @method: the method name
|
|
*
|
|
* Search in the __cpuidle_method_of_table array the cpuidle ops matching the
|
|
* method name.
|
|
*
|
|
* Returns a struct cpuidle_ops pointer, NULL if not found.
|
|
*/
|
|
static const struct cpuidle_ops *__init arm_cpuidle_get_ops(const char *method)
|
|
{
|
|
struct of_cpuidle_method *m = __cpuidle_method_of_table;
|
|
|
|
for (; m->method; m++)
|
|
if (!strcmp(m->method, method))
|
|
return m->ops;
|
|
|
|
return NULL;
|
|
}
|
|
|
|
/**
|
|
* arm_cpuidle_read_ops() - Initialize the cpuidle ops with the device tree
|
|
* @dn: a pointer to a struct device node corresponding to a cpu node
|
|
* @cpu: the cpu identifier
|
|
*
|
|
* Get the method name defined in the 'enable-method' property, retrieve the
|
|
* associated cpuidle_ops and do a struct copy. This copy is needed because all
|
|
* cpuidle_ops are tagged __initconst and will be unloaded after the init
|
|
* process.
|
|
*
|
|
* Return 0 on sucess, -ENOENT if no 'enable-method' is defined, -EOPNOTSUPP if
|
|
* no cpuidle_ops is registered for the 'enable-method', or if either init or
|
|
* suspend callback isn't defined.
|
|
*/
|
|
static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu)
|
|
{
|
|
const char *enable_method;
|
|
const struct cpuidle_ops *ops;
|
|
|
|
enable_method = of_get_property(dn, "enable-method", NULL);
|
|
if (!enable_method)
|
|
return -ENOENT;
|
|
|
|
ops = arm_cpuidle_get_ops(enable_method);
|
|
if (!ops) {
|
|
pr_warn("%pOF: unsupported enable-method property: %s\n",
|
|
dn, enable_method);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
if (!ops->init || !ops->suspend) {
|
|
pr_warn("cpuidle_ops '%s': no init or suspend callback\n",
|
|
enable_method);
|
|
return -EOPNOTSUPP;
|
|
}
|
|
|
|
cpuidle_ops[cpu] = *ops; /* structure copy */
|
|
|
|
pr_notice("cpuidle: enable-method property '%s'"
|
|
" found operations\n", enable_method);
|
|
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* arm_cpuidle_init() - Initialize cpuidle_ops for a specific cpu
|
|
* @cpu: the cpu to be initialized
|
|
*
|
|
* Initialize the cpuidle ops with the device for the cpu and then call
|
|
* the cpu's idle initialization callback. This may fail if the underlying HW
|
|
* is not operational.
|
|
*
|
|
* Returns:
|
|
* 0 on success,
|
|
* -ENODEV if it fails to find the cpu node in the device tree,
|
|
* -EOPNOTSUPP if it does not find a registered and valid cpuidle_ops for
|
|
* this cpu,
|
|
* -ENOENT if it fails to find an 'enable-method' property,
|
|
* -ENXIO if the HW reports a failure or a misconfiguration,
|
|
* -ENOMEM if the HW report an memory allocation failure
|
|
*/
|
|
int __init arm_cpuidle_init(int cpu)
|
|
{
|
|
struct device_node *cpu_node = of_cpu_device_node_get(cpu);
|
|
int ret;
|
|
|
|
if (!cpu_node)
|
|
return -ENODEV;
|
|
|
|
ret = arm_cpuidle_read_ops(cpu_node, cpu);
|
|
if (!ret)
|
|
ret = cpuidle_ops[cpu].init(cpu_node, cpu);
|
|
|
|
of_node_put(cpu_node);
|
|
|
|
return ret;
|
|
}
|