From 830b8519a41c5b13b1787ef94f31ad1d3507ec73 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 14 Feb 2014 14:28:39 +0000 Subject: [PATCH 1/5] drivers: cpuidle: implement OF based idle states infrastructure On most common ARM systems, the low-power states a CPU can be put into are not discoverable in HW and require device tree bindings to describe the respective power domains, power down protocol and idle states parameters. In order to enable DT based idle states and configure idle drivers, this patch implements the bulk infrastructure required to parse the device tree idle states bindings and initialize the corresponding CPUidle driver states data. Code that initializes idle states checks the CPU idle driver cpumask so that multiple CPU idle drivers can be initialized through it in the kernel. The CPU idle driver cpumask defines which idle states should be considered valid for the driver, ie idle states that are valid on a set of cpus the idle driver manages. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Mark Brown Conflicts: drivers/cpuidle/Kconfig --- drivers/cpuidle/Kconfig | 9 + drivers/cpuidle/Makefile | 1 + drivers/cpuidle/of_idle_states.c | 274 +++++++++++++++++++++++++++++++ drivers/cpuidle/of_idle_states.h | 8 + 4 files changed, 292 insertions(+) create mode 100644 drivers/cpuidle/of_idle_states.c create mode 100644 drivers/cpuidle/of_idle_states.h diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index c4cc27e5c8a5..e4d3913fe330 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -31,6 +31,15 @@ config CPU_IDLE_GOV_MENU config ARCH_NEEDS_CPU_IDLE_COUPLED def_bool n +config OF_IDLE_STATES + bool "Idle states DT support" + depends on ARM || ARM64 + default n + help + Allows the CPU idle framework to initialize CPU idle drivers + state data by using DT provided nodes compliant with idle states + device tree bindings. + if CPU_IDLE config CPU_IDLE_CALXEDA diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 0d8bd55e776f..d41366a7838c 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -4,6 +4,7 @@ obj-y += cpuidle.o driver.o governor.o sysfs.o governors/ obj-$(CONFIG_ARCH_NEEDS_CPU_IDLE_COUPLED) += coupled.o +obj-$(CONFIG_OF_IDLE_STATES) += of_idle_states.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o diff --git a/drivers/cpuidle/of_idle_states.c b/drivers/cpuidle/of_idle_states.c new file mode 100644 index 000000000000..eceb1b4c4657 --- /dev/null +++ b/drivers/cpuidle/of_idle_states.c @@ -0,0 +1,274 @@ +/* + * OF idle states parsing code. + * + * Copyright (C) 2014 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "of_idle_states.h" + +struct state_elem { + struct list_head list; + struct device_node *node; + int val; +}; + +static struct list_head head __initdata = LIST_HEAD_INIT(head); + +static bool __init state_cpu_valid(struct device_node *state_node, + struct device_node *cpu_node) +{ + int i = 0; + struct device_node *cpu_state; + + while ((cpu_state = of_parse_phandle(cpu_node, + "cpu-idle-states", i++))) { + if (cpu_state && state_node == cpu_state) { + of_node_put(cpu_state); + return true; + } + of_node_put(cpu_state); + } + return false; +} + +static bool __init state_cpus_valid(const cpumask_t *cpus, + struct device_node *state_node) +{ + int cpu; + struct device_node *cpu_node; + + /* + * Check if state is valid on driver cpumask cpus + */ + for_each_cpu(cpu, cpus) { + cpu_node = of_get_cpu_node(cpu, NULL); + + if (!cpu_node) { + pr_err("Missing device node for CPU %d\n", cpu); + return false; + } + + if (!state_cpu_valid(state_node, cpu_node)) + return false; + } + + return true; +} + +static int __init state_cmp(void *priv, struct list_head *a, + struct list_head *b) +{ + struct state_elem *ela, *elb; + + ela = container_of(a, struct state_elem, list); + elb = container_of(b, struct state_elem, list); + + return ela->val - elb->val; +} + +static int __init add_state_node(cpumask_t *cpumask, + struct device_node *state_node) +{ + struct state_elem *el; + u32 val; + + pr_debug(" * %s...\n", state_node->full_name); + + if (!state_cpus_valid(cpumask, state_node)) + return -EINVAL; + /* + * Parse just the value required to sort the states. + */ + if (of_property_read_u32(state_node, "min-residency-us", + &val)) { + pr_debug(" * %s missing min-residency-us property\n", + state_node->full_name); + return -EINVAL; + } + + el = kmalloc(sizeof(*el), GFP_KERNEL); + if (!el) { + pr_err("%s failed to allocate memory\n", __func__); + return -ENOMEM; + } + + el->node = state_node; + el->val = val; + list_add_tail(&el->list, &head); + + return 0; +} + +static void __init init_state_node(struct cpuidle_driver *drv, + struct device_node *state_node, + int *cnt) +{ + struct cpuidle_state *idle_state; + + pr_debug(" * %s...\n", state_node->full_name); + + idle_state = &drv->states[*cnt]; + + if (of_property_read_u32(state_node, "exit-latency-us", + &idle_state->exit_latency)) { + pr_debug(" * %s missing exit-latency-us property\n", + state_node->full_name); + return; + } + + if (of_property_read_u32(state_node, "min-residency-us", + &idle_state->target_residency)) { + pr_debug(" * %s missing min-residency-us property\n", + state_node->full_name); + return; + } + /* + * It is unknown to the idle driver if and when the tick_device + * loses context when the CPU enters the idle states. To solve + * this issue the tick device must be linked to a power domain + * so that the idle driver can check on which states the device + * loses its context. Current code takes the conservative choice + * of defining the idle state as one where the tick device always + * loses its context. On platforms where tick device never loses + * its context (ie it is not a C3STOP device) this turns into + * a nop. On platforms where the tick device does lose context in some + * states, this code can be optimized, when power domain specifications + * for ARM CPUs are finalized. + */ + idle_state->flags = CPUIDLE_FLAG_TIME_VALID | CPUIDLE_FLAG_TIMER_STOP; + + strncpy(idle_state->name, state_node->name, CPUIDLE_NAME_LEN); + strncpy(idle_state->desc, state_node->name, CPUIDLE_NAME_LEN); + + (*cnt)++; +} + +static int __init init_idle_states(struct cpuidle_driver *drv, + struct device_node *state_nodes[], + unsigned int start_idx, bool init_nodes) +{ + struct state_elem *el; + struct list_head *curr, *tmp; + unsigned int cnt = start_idx; + + list_for_each_entry(el, &head, list) { + /* + * Check if the init function has to fill the + * state_nodes array on behalf of the CPUidle driver. + */ + if (init_nodes) + state_nodes[cnt] = el->node; + /* + * cnt is updated on return if a state was added. + */ + init_state_node(drv, el->node, &cnt); + + if (cnt == CPUIDLE_STATE_MAX) { + pr_warn("State index reached static CPU idle state limit\n"); + break; + } + } + + drv->state_count = cnt; + + list_for_each_safe(curr, tmp, &head) { + list_del(curr); + kfree(container_of(curr, struct state_elem, list)); + } + + /* + * If no idle states are detected, return an error and let the idle + * driver initialization fail accordingly. + */ + return (cnt > start_idx) ? 0 : -ENODATA; +} + +static void __init add_idle_states(struct cpuidle_driver *drv, + struct device_node *idle_states) +{ + struct device_node *state_node; + + for_each_child_of_node(idle_states, state_node) { + if ((!of_device_is_compatible(state_node, "arm,idle-state"))) { + pr_warn(" * %s: children of /cpus/idle-states must be \"arm,idle-state\" compatible\n", + state_node->full_name); + continue; + } + /* + * If memory allocation fails, better bail out. + * Initialized nodes are freed at initialization + * completion in of_init_idle_driver(). + */ + if ((add_state_node(drv->cpumask, state_node) == -ENOMEM)) + break; + } + /* + * Sort the states list before initializing the CPUidle driver + * states array. + */ + list_sort(NULL, &head, state_cmp); +} + +/* + * of_init_idle_driver - Parse the DT idle states and initialize the + * idle driver states array + * + * @drv: Pointer to CPU idle driver to be initialized + * @state_nodes: Array of struct device_nodes to be initialized if + * init_nodes == true. Must be sized CPUIDLE_STATE_MAX + * @start_idx: First idle state index to be initialized + * @init_nodes: Boolean to request device nodes initialization + * + * Returns: + * 0 on success + * <0 on failure + * + * On success the states array in the cpuidle driver contains + * initialized entries in the states array, starting from index start_idx. + * If init_nodes == true, on success the state_nodes array is initialized + * with idle state DT node pointers, starting from index start_idx, + * in a 1:1 relation with the idle driver states array. + */ +int __init of_init_idle_driver(struct cpuidle_driver *drv, + struct device_node *state_nodes[], + unsigned int start_idx, bool init_nodes) +{ + struct device_node *idle_states_node; + int ret; + + if (start_idx >= CPUIDLE_STATE_MAX) { + pr_warn("State index exceeds static CPU idle driver states array size\n"); + return -EINVAL; + } + + if (WARN(init_nodes && !state_nodes, + "Requested nodes stashing in an invalid nodes container\n")) + return -EINVAL; + + idle_states_node = of_find_node_by_path("/cpus/idle-states"); + if (!idle_states_node) + return -ENOENT; + + add_idle_states(drv, idle_states_node); + + ret = init_idle_states(drv, state_nodes, start_idx, init_nodes); + + of_node_put(idle_states_node); + + return ret; +} diff --git a/drivers/cpuidle/of_idle_states.h b/drivers/cpuidle/of_idle_states.h new file mode 100644 index 000000000000..049f94ff4428 --- /dev/null +++ b/drivers/cpuidle/of_idle_states.h @@ -0,0 +1,8 @@ +#ifndef __OF_IDLE_STATES +#define __OF_IDLE_STATES + +int __init of_init_idle_driver(struct cpuidle_driver *drv, + struct device_node *state_nodes[], + unsigned int start_idx, + bool init_nodes); +#endif From 75ce30ddaa79134bd0225a5834016acd7cbb62ac Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 27 Sep 2013 10:25:02 +0100 Subject: [PATCH 2/5] arm64: add PSCI CPU_SUSPEND based cpu_suspend support This patch implements the cpu_suspend cpu operations method through the PSCI CPU_SUSPEND API. The PSCI implementation translates the idle state index passed by the cpu_suspend core call into a valid PSCI state according to the PSCI states initialized at boot by the PSCI suspend backend. Entry point is set to cpu_resume physical address, that represents the default kernel execution address following a CPU reset. Idle state indices missing a DT node description are initialized to power state standby WFI so that if called by the idle driver they provide the default behaviour. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Mark Brown --- arch/arm64/include/asm/psci.h | 4 ++ arch/arm64/kernel/psci.c | 102 ++++++++++++++++++++++++++++++++++ 2 files changed, 106 insertions(+) diff --git a/arch/arm64/include/asm/psci.h b/arch/arm64/include/asm/psci.h index d15ab8b46336..9a4b663670ff 100644 --- a/arch/arm64/include/asm/psci.h +++ b/arch/arm64/include/asm/psci.h @@ -14,6 +14,10 @@ #ifndef __ASM_PSCI_H #define __ASM_PSCI_H +struct cpuidle_driver; void psci_init(void); +int __init psci_dt_register_idle_states(struct cpuidle_driver *, + struct device_node *[]); + #endif /* __ASM_PSCI_H */ diff --git a/arch/arm64/kernel/psci.c b/arch/arm64/kernel/psci.c index ea4828a4aa96..0e32ab453e5b 100644 --- a/arch/arm64/kernel/psci.c +++ b/arch/arm64/kernel/psci.c @@ -15,15 +15,18 @@ #define pr_fmt(fmt) "psci: " fmt +#include #include #include #include +#include #include #include #include #include #include +#include #define PSCI_POWER_STATE_TYPE_STANDBY 0 #define PSCI_POWER_STATE_TYPE_POWER_DOWN 1 @@ -54,6 +57,8 @@ enum psci_function { PSCI_FN_MAX, }; +static DEFINE_PER_CPU_READ_MOSTLY(struct psci_power_state *, psci_power_state); + static u32 psci_function_id[PSCI_FN_MAX]; #define PSCI_RET_SUCCESS 0 @@ -94,6 +99,17 @@ static u32 psci_power_state_pack(struct psci_power_state state) << PSCI_POWER_STATE_AFFL_SHIFT); } +static void psci_power_state_unpack(u32 power_state, + struct psci_power_state *state) +{ + state->id = (power_state >> PSCI_POWER_STATE_ID_SHIFT) + & PSCI_POWER_STATE_ID_MASK; + state->type = (power_state >> PSCI_POWER_STATE_TYPE_SHIFT) + & PSCI_POWER_STATE_TYPE_MASK; + state->affinity_level = (power_state >> PSCI_POWER_STATE_AFFL_SHIFT) + & PSCI_POWER_STATE_AFFL_MASK; +} + /* * The following two functions are invoked via the invoke_psci_fn pointer * and will not be inlined, allowing us to piggyback on the AAPCS. @@ -176,6 +192,77 @@ static const struct of_device_id psci_of_match[] __initconst = { {}, }; +int __init psci_dt_register_idle_states(struct cpuidle_driver *drv, + struct device_node *state_nodes[]) +{ + int cpu, i; + struct psci_power_state *psci_states; + const struct cpu_operations *cpu_ops_ptr; + + if (!state_nodes) + return -EINVAL; + /* + * This is belt-and-braces: make sure that if the idle + * specified protocol is psci, the cpu_ops have been + * initialized to psci operations. Anything else is + * a recipe for mayhem. + */ + for_each_cpu(cpu, drv->cpumask) { + cpu_ops_ptr = cpu_ops[cpu]; + if (WARN_ON(!cpu_ops_ptr || strcmp(cpu_ops_ptr->name, "psci"))) + return -EOPNOTSUPP; + } + + psci_states = kcalloc(drv->state_count, sizeof(*psci_states), + GFP_KERNEL); + + if (!psci_states) { + pr_warn("psci idle state allocation failed\n"); + return -ENOMEM; + } + + for_each_cpu(cpu, drv->cpumask) { + if (per_cpu(psci_power_state, cpu)) { + pr_warn("idle states already initialized on cpu %u\n", + cpu); + continue; + } + per_cpu(psci_power_state, cpu) = psci_states; + } + + + for (i = 0; i < drv->state_count; i++) { + u32 psci_power_state; + + if (!state_nodes[i]) { + /* + * An index with a missing node pointer falls back to + * simple STANDBYWFI + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + if (of_property_read_u32(state_nodes[i], "entry-method-param", + &psci_power_state)) { + pr_warn(" * %s missing entry-method-param property\n", + state_nodes[i]->full_name); + /* + * If entry-method-param property is missing, fall + * back to STANDBYWFI state + */ + psci_states[i].type = PSCI_POWER_STATE_TYPE_STANDBY; + continue; + } + + pr_debug("psci-power-state %#x index %u\n", + psci_power_state, i); + psci_power_state_unpack(psci_power_state, &psci_states[i]); + } + + return 0; +} + void __init psci_init(void) { struct device_node *np; @@ -279,6 +366,18 @@ static void cpu_psci_cpu_die(unsigned int cpu) } #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND +static int cpu_psci_cpu_suspend(unsigned long index) +{ + struct psci_power_state *state = __get_cpu_var(psci_power_state); + + if (!state) + return -EOPNOTSUPP; + + return psci_ops.cpu_suspend(state[index], virt_to_phys(cpu_resume)); +} +#endif + const struct cpu_operations cpu_psci_ops = { .name = "psci", .cpu_init = cpu_psci_cpu_init, @@ -288,6 +387,9 @@ const struct cpu_operations cpu_psci_ops = { .cpu_disable = cpu_psci_cpu_disable, .cpu_die = cpu_psci_cpu_die, #endif +#ifdef CONFIG_ARM64_CPU_SUSPEND + .cpu_suspend = cpu_psci_cpu_suspend, +#endif }; #endif From 08461b57c19fb68dde8907b87ff38f38f2585f64 Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 28 Feb 2014 13:03:44 +0000 Subject: [PATCH 3/5] drivers: cpuidle: CPU idle ARM64 driver This patch implements a generic CPU idle driver for ARM64 machines. It relies on the DT idle states infrastructure to initialize idle states count and respective parameters. Current code assumes the driver is managing idle states on all possible CPUs but can be easily generalized to support heterogenous systems and build cpumasks at runtime using MIDRs or DT cpu nodes compatible properties. Suspend back-ends (eg PSCI) must register a suspend initializer with the CPU idle driver so that the suspend backend call can be detected, and the driver code can call the back-end infrastructure to complete the suspend backend initialization. Idle state index 0 is always initialized as a simple wfi state, ie always considered present and functional on all ARM64 platforms. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Mark Brown Conflicts: drivers/cpuidle/Kconfig drivers/cpuidle/Makefile --- drivers/cpuidle/Kconfig | 5 + drivers/cpuidle/Kconfig.arm64 | 13 +++ drivers/cpuidle/Makefile | 4 + drivers/cpuidle/cpuidle-arm64.c | 159 ++++++++++++++++++++++++++++++++ 4 files changed, 181 insertions(+) create mode 100644 drivers/cpuidle/Kconfig.arm64 create mode 100644 drivers/cpuidle/cpuidle-arm64.c diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index e4d3913fe330..842d7ba83101 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -48,4 +48,9 @@ config CPU_IDLE_CALXEDA help Select this to enable cpuidle on Calxeda processors. +menu "ARM64 CPU Idle Drivers" +depends on ARM64 +source "drivers/cpuidle/Kconfig.arm64" +endmenu + endif diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 new file mode 100644 index 000000000000..b83612c67e6d --- /dev/null +++ b/drivers/cpuidle/Kconfig.arm64 @@ -0,0 +1,13 @@ +# +# ARM64 CPU Idle drivers +# + +config ARM64_CPUIDLE + bool "Generic ARM64 CPU idle Driver" + select OF_IDLE_STATES + help + Select this to enable generic cpuidle driver for ARM v8. + It provides a generic idle driver whose idle states are configured + at run-time through DT nodes. The CPUidle suspend backend is + initialized by the device tree parsing code on matching the entry + method to the respective CPU operations. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index d41366a7838c..d3e662cde224 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -8,3 +8,7 @@ obj-$(CONFIG_OF_IDLE_STATES) += of_idle_states.o obj-$(CONFIG_CPU_IDLE_CALXEDA) += cpuidle-calxeda.o obj-$(CONFIG_ARCH_KIRKWOOD) += cpuidle-kirkwood.o + +############################################################################### +# ARM64 drivers +obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c new file mode 100644 index 000000000000..2cfde6ce3086 --- /dev/null +++ b/drivers/cpuidle/cpuidle-arm64.c @@ -0,0 +1,159 @@ +/* + * ARM64 generic CPU idle driver. + * + * Copyright (C) 2014 ARM Ltd. + * Author: Lorenzo Pieralisi + * + * 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. + */ + +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "of_idle_states.h" + +typedef int (*suspend_init_fn)(struct cpuidle_driver *, + struct device_node *[]); + +struct cpu_suspend_ops { + const char *id; + suspend_init_fn init_fn; +}; + +static const struct cpu_suspend_ops suspend_operations[] __initconst = { + {"arm,psci", psci_dt_register_idle_states}, + {} +}; + +static __init const struct cpu_suspend_ops *get_suspend_ops(const char *str) +{ + int i; + + if (!str) + return NULL; + + for (i = 0; suspend_operations[i].id; i++) + if (!strcmp(suspend_operations[i].id, str)) + return &suspend_operations[i]; + + return NULL; +} + +/* + * arm_enter_idle_state - Programs CPU to enter the specified state + * + * @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. + */ +static int arm_enter_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) +{ + int ret; + + if (!idx) { + cpu_do_idle(); + return idx; + } + + cpu_pm_enter(); + /* + * Pass idle state index to cpu_suspend which in turn will call + * the CPU ops suspend protocol with idle index as a parameter. + * + * Some states would not require context to be saved and flushed + * to DRAM, so calling cpu_suspend would not be stricly necessary. + * When power domains specifications for ARM CPUs are finalized then + * this code can be optimized to prevent saving registers if not + * needed. + */ + ret = cpu_suspend(idx); + + cpu_pm_exit(); + + return ret ? -1 : idx; +} + +struct cpuidle_driver arm64_idle_driver = { + .name = "arm64_idle", + .owner = THIS_MODULE, +}; + +static struct device_node *state_nodes[CPUIDLE_STATE_MAX] __initdata; + +/* + * arm64_idle_init + * + * Registers the arm64 specific cpuidle driver with the cpuidle + * framework. It relies on core code to parse the idle states + * and initialize them using driver data structures accordingly. + */ +static int __init arm64_idle_init(void) +{ + int i, ret; + const char *entry_method; + struct device_node *idle_states_node; + const struct cpu_suspend_ops *suspend_init; + struct cpuidle_driver *drv = &arm64_idle_driver; + + idle_states_node = of_find_node_by_path("/cpus/idle-states"); + if (!idle_states_node) + return -ENOENT; + + if (of_property_read_string(idle_states_node, "entry-method", + &entry_method)) { + pr_warn(" * %s missing entry-method property\n", + idle_states_node->full_name); + of_node_put(idle_states_node); + return -EOPNOTSUPP; + } + + suspend_init = get_suspend_ops(entry_method); + if (!suspend_init) { + pr_warn("Missing suspend initializer\n"); + of_node_put(idle_states_node); + return -EOPNOTSUPP; + } + + /* + * 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. + */ + drv->states[0].exit_latency = 1; + drv->states[0].target_residency = 1; + drv->states[0].flags = CPUIDLE_FLAG_TIME_VALID; + strncpy(drv->states[0].name, "ARM WFI", CPUIDLE_NAME_LEN); + strncpy(drv->states[0].desc, "ARM WFI", CPUIDLE_DESC_LEN); + + drv->cpumask = (struct cpumask *) cpu_possible_mask; + /* + * Start at index 1, request idle state nodes to be filled + */ + ret = of_init_idle_driver(drv, state_nodes, 1, true); + if (ret) + return ret; + + if (suspend_init->init_fn(drv, state_nodes)) + return -EOPNOTSUPP; + + for (i = 0; i < drv->state_count; i++) + drv->states[i].enter = arm_enter_idle_state; + + return cpuidle_register(drv, NULL); +} +device_initcall(arm64_idle_init); From f43eb69268fef0aad987f58860d0fd835bfaef5b Mon Sep 17 00:00:00 2001 From: Lorenzo Pieralisi Date: Fri, 14 Mar 2014 11:10:35 +0000 Subject: [PATCH 4/5] arm64: boot: dts: update rtsm aemv8 dts with PSCI and idle states This patch updates the RTSM dts file with PSCI bindings and nodes describing the AEMv8 model idle states parameters. PSCI function IDs compliancy with PSCI v0.2 is still under development so this patch provides PSCI function IDs for demonstration purposes only. Signed-off-by: Lorenzo Pieralisi Signed-off-by: Mark Brown --- arch/arm64/boot/dts/rtsm_ve-aemv8a.dts | 49 +++++++++++++++++++++----- 1 file changed, 41 insertions(+), 8 deletions(-) diff --git a/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts b/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts index 572005ea2217..410a62c08530 100644 --- a/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts +++ b/arch/arm64/boot/dts/rtsm_ve-aemv8a.dts @@ -27,37 +27,70 @@ serial3 = &v2m_serial3; }; + psci { + compatible = "arm,psci"; + method = "smc"; + /* + * Function IDs usage and compliancy with PSCI v0.2 still + * under discussion. Current IDs should be considered + * temporary for demonstration purposes. + */ + cpu_suspend = <0x84000001>; + cpu_off = <0x84000002>; + cpu_on = <0x84000003>; + }; + cpus { #address-cells = <2>; #size-cells = <0>; + idle-states { + entry-method = "arm,psci"; + + CPU_SLEEP_0: cpu-sleep-0 { + compatible = "arm,idle-state"; + entry-method-param = <0x0010000>; + entry-latency-us = <40>; + exit-latency-us = <100>; + min-residency-us = <150>; + }; + + CLUSTER_SLEEP_0: cluster-sleep-0 { + compatible = "arm,idle-state"; + entry-method-param = <0x1010000>; + entry-latency-us = <500>; + exit-latency-us = <1000>; + min-residency-us = <2500>; + }; + }; + cpu@0 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x0>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; cpu@1 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x1>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; cpu@2 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x2>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; cpu@3 { device_type = "cpu"; compatible = "arm,armv8"; reg = <0x0 0x3>; - enable-method = "spin-table"; - cpu-release-addr = <0x0 0x8000fff8>; + enable-method = "psci"; + cpu-idle-states = <&CPU_SLEEP_0 &CLUSTER_SLEEP_0>; }; }; From ce2bf4a89734e34022800b122f320186e35b188e Mon Sep 17 00:00:00 2001 From: Mark Brown Date: Thu, 29 May 2014 23:05:18 +0100 Subject: [PATCH 5/5] cpuidle: Add cpumask to ease backporting To make backporting a little easier provide a cpumask variable in the driver struct - the core won't do anything with it but it's less code to maintain. Signed-off-by: Mark Brown --- include/linux/cpuidle.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/linux/cpuidle.h b/include/linux/cpuidle.h index 8f0406230a0a..1312cde68504 100644 --- a/include/linux/cpuidle.h +++ b/include/linux/cpuidle.h @@ -111,6 +111,7 @@ struct cpuidle_driver { struct cpuidle_state states[CPUIDLE_STATE_MAX]; int state_count; int safe_state_index; + struct cpumask *cpumask; }; #ifdef CONFIG_CPU_IDLE