diff options
author | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-03-25 14:22:37 +0300 |
---|---|---|
committer | Rafael J. Wysocki <rafael.j.wysocki@intel.com> | 2015-03-25 14:22:37 +0300 |
commit | 993c592d745c467d83e753c9e8f4e9f952381478 (patch) | |
tree | 9f29da7fa458f6a6a88048a14e7f510edfb84fa7 | |
parent | bc465aa9d045feb0e13b4a8f32cc33c1943f62d6 (diff) | |
parent | 9a309d6fd213911321acbfe839e0bdb3a7a9f4bf (diff) | |
download | linux-993c592d745c467d83e753c9e8f4e9f952381478.tar.xz |
Merge branch 'cpuidle/4.1' of http://git.linaro.org/people/daniel.lezcano/linux into pm-cpuidle
Pull ARM cpuidle changes for v4.1 from Daniel Lezcano.
* 'cpuidle/4.1' of http://git.linaro.org/people/daniel.lezcano/linux:
ARM: cpuidle: Document the code
ARM: cpuidle: Register per cpuidle device
ARM: cpuidle: Enable the ARM64 driver for both ARM32/ARM64
ARM64: cpuidle: Remove arm64 reference
ARM64: cpuidle: Rename cpu_init_idle to a common function name
ARM64: cpuidle: Replace cpu_suspend by the common ARM/ARM64 function
ARM: cpuidle: Add a cpuidle ops structure to be used for DT
ARM: cpuidle: Remove duplicate header inclusion
24 files changed, 249 insertions, 72 deletions
diff --git a/arch/arm/include/asm/cpuidle.h b/arch/arm/include/asm/cpuidle.h index af319ac4960c..0f8424924902 100644 --- a/arch/arm/include/asm/cpuidle.h +++ b/arch/arm/include/asm/cpuidle.h @@ -1,6 +1,8 @@ #ifndef __ASM_ARM_CPUIDLE_H #define __ASM_ARM_CPUIDLE_H +#include <asm/proc-fns.h> + #ifdef CONFIG_CPU_IDLE extern int arm_cpuidle_simple_enter(struct cpuidle_device *dev, struct cpuidle_driver *drv, int index); @@ -25,4 +27,25 @@ static inline int arm_cpuidle_simple_enter(struct cpuidle_device *dev, */ #define ARM_CPUIDLE_WFI_STATE ARM_CPUIDLE_WFI_STATE_PWR(UINT_MAX) +struct device_node; + +struct cpuidle_ops { + int (*suspend)(int cpu, unsigned long arg); + int (*init)(struct device_node *, int cpu); +}; + +struct of_cpuidle_method { + const char *method; + struct cpuidle_ops *ops; +}; + +#define CPUIDLE_METHOD_OF_DECLARE(name, _method, _ops) \ + static const struct of_cpuidle_method __cpuidle_method_of_table_##name \ + __used __section(__cpuidle_method_of_table) \ + = { .method = _method, .ops = _ops } + +extern int arm_cpuidle_suspend(int index); + +extern int arm_cpuidle_init(int cpu); + #endif diff --git a/arch/arm/kernel/cpuidle.c b/arch/arm/kernel/cpuidle.c index 89545f6c8403..318da33465f4 100644 --- a/arch/arm/kernel/cpuidle.c +++ b/arch/arm/kernel/cpuidle.c @@ -10,8 +10,28 @@ */ #include <linux/cpuidle.h> -#include <asm/proc-fns.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]; + +/** + * 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) { @@ -19,3 +39,114 @@ int arm_cpuidle_simple_enter(struct cpuidle_device *dev, 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 -EOPNOTSUPP if no suspend callback is defined, the result of the + * callback otherwise. + */ +int arm_cpuidle_suspend(int index) +{ + int ret = -EOPNOTSUPP; + int cpu = smp_processor_id(); + + if (cpuidle_ops[cpu].suspend) + ret = cpuidle_ops[cpu].suspend(cpu, index); + + return ret; +} + +/** + * 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 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 __initdata 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'. + */ +static int __init arm_cpuidle_read_ops(struct device_node *dn, int cpu) +{ + const char *enable_method; + 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("%s: unsupported enable-method property: %s\n", + dn->full_name, 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 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 && cpuidle_ops[cpu].init) + ret = cpuidle_ops[cpu].init(cpu_node, cpu); + + of_node_put(cpu_node); + + return ret; +} diff --git a/arch/arm/mach-davinci/cpuidle.c b/arch/arm/mach-davinci/cpuidle.c index e365c1bb1265..306ebc51599a 100644 --- a/arch/arm/mach-davinci/cpuidle.c +++ b/arch/arm/mach-davinci/cpuidle.c @@ -17,7 +17,6 @@ #include <linux/cpuidle.h> #include <linux/io.h> #include <linux/export.h> -#include <asm/proc-fns.h> #include <asm/cpuidle.h> #include <mach/cpuidle.h> diff --git a/arch/arm/mach-imx/cpuidle-imx6q.c b/arch/arm/mach-imx/cpuidle-imx6q.c index d76d08623f9f..8e21ccc1eda2 100644 --- a/arch/arm/mach-imx/cpuidle-imx6q.c +++ b/arch/arm/mach-imx/cpuidle-imx6q.c @@ -9,7 +9,6 @@ #include <linux/cpuidle.h> #include <linux/module.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include "common.h" #include "cpuidle.h" diff --git a/arch/arm/mach-imx/cpuidle-imx6sl.c b/arch/arm/mach-imx/cpuidle-imx6sl.c index 7d92e6584551..5742a9fd1ef2 100644 --- a/arch/arm/mach-imx/cpuidle-imx6sl.c +++ b/arch/arm/mach-imx/cpuidle-imx6sl.c @@ -9,7 +9,6 @@ #include <linux/cpuidle.h> #include <linux/module.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include "common.h" #include "cpuidle.h" diff --git a/arch/arm/mach-imx/cpuidle-imx6sx.c b/arch/arm/mach-imx/cpuidle-imx6sx.c index 5a36722b089d..2c9f1a8bf245 100644 --- a/arch/arm/mach-imx/cpuidle-imx6sx.c +++ b/arch/arm/mach-imx/cpuidle-imx6sx.c @@ -10,7 +10,6 @@ #include <linux/cpu_pm.h> #include <linux/module.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include <asm/suspend.h> #include "common.h" diff --git a/arch/arm/mach-omap2/cpuidle44xx.c b/arch/arm/mach-omap2/cpuidle44xx.c index 01e398a868bc..7622dbb05083 100644 --- a/arch/arm/mach-omap2/cpuidle44xx.c +++ b/arch/arm/mach-omap2/cpuidle44xx.c @@ -17,7 +17,6 @@ #include <linux/clockchips.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include "common.h" #include "pm.h" diff --git a/arch/arm/mach-s3c64xx/cpuidle.c b/arch/arm/mach-s3c64xx/cpuidle.c index 2eb072440dfa..93aa8cb70195 100644 --- a/arch/arm/mach-s3c64xx/cpuidle.c +++ b/arch/arm/mach-s3c64xx/cpuidle.c @@ -16,7 +16,7 @@ #include <linux/export.h> #include <linux/time.h> -#include <asm/proc-fns.h> +#include <asm/cpuidle.h> #include <mach/map.h> diff --git a/arch/arm/mach-tegra/cpuidle-tegra20.c b/arch/arm/mach-tegra/cpuidle-tegra20.c index 4f25a7c7ca0f..e22b0d9fdc88 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra20.c +++ b/arch/arm/mach-tegra/cpuidle-tegra20.c @@ -27,7 +27,6 @@ #include <linux/module.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include <asm/smp_plat.h> #include <asm/suspend.h> diff --git a/arch/arm/mach-tegra/cpuidle-tegra30.c b/arch/arm/mach-tegra/cpuidle-tegra30.c index f8815ed65d9d..a2400ab44daa 100644 --- a/arch/arm/mach-tegra/cpuidle-tegra30.c +++ b/arch/arm/mach-tegra/cpuidle-tegra30.c @@ -27,7 +27,6 @@ #include <linux/module.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> #include <asm/smp_plat.h> #include <asm/suspend.h> diff --git a/arch/arm64/configs/defconfig b/arch/arm64/configs/defconfig index be1f12a5a5f0..af6a452b1aac 100644 --- a/arch/arm64/configs/defconfig +++ b/arch/arm64/configs/defconfig @@ -48,7 +48,7 @@ CONFIG_CMDLINE="console=ttyAMA0" # CONFIG_CORE_DUMP_DEFAULT_ELF_HEADERS is not set CONFIG_COMPAT=y CONFIG_CPU_IDLE=y -CONFIG_ARM64_CPUIDLE=y +CONFIG_ARM_CPUIDLE=y CONFIG_NET=y CONFIG_PACKET=y CONFIG_UNIX=y diff --git a/arch/arm64/include/asm/cpuidle.h b/arch/arm64/include/asm/cpuidle.h index c60643f14cda..141b2fcabaa6 100644 --- a/arch/arm64/include/asm/cpuidle.h +++ b/arch/arm64/include/asm/cpuidle.h @@ -4,10 +4,10 @@ #include <asm/proc-fns.h> #ifdef CONFIG_CPU_IDLE -extern int cpu_init_idle(unsigned int cpu); +extern int arm_cpuidle_init(unsigned int cpu); extern int cpu_suspend(unsigned long arg); #else -static inline int cpu_init_idle(unsigned int cpu) +static inline int arm_cpuidle_init(unsigned int cpu) { return -EOPNOTSUPP; } @@ -17,5 +17,8 @@ static inline int cpu_suspend(unsigned long arg) return -EOPNOTSUPP; } #endif - +static inline int arm_cpuidle_suspend(int index) +{ + return cpu_suspend(index); +} #endif diff --git a/arch/arm64/kernel/cpuidle.c b/arch/arm64/kernel/cpuidle.c index 5c0896647fd1..a78143a5c99f 100644 --- a/arch/arm64/kernel/cpuidle.c +++ b/arch/arm64/kernel/cpuidle.c @@ -15,7 +15,7 @@ #include <asm/cpuidle.h> #include <asm/cpu_ops.h> -int cpu_init_idle(unsigned int cpu) +int arm_cpuidle_init(unsigned int cpu) { int ret = -EOPNOTSUPP; struct device_node *cpu_node = of_cpu_device_node_get(cpu); diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig index c5029c1209b4..8c7930b5a65f 100644 --- a/drivers/cpuidle/Kconfig +++ b/drivers/cpuidle/Kconfig @@ -29,15 +29,10 @@ config DT_IDLE_STATES bool menu "ARM CPU Idle Drivers" -depends on ARM +depends on ARM || ARM64 source "drivers/cpuidle/Kconfig.arm" endmenu -menu "ARM64 CPU Idle Drivers" -depends on ARM64 -source "drivers/cpuidle/Kconfig.arm64" -endmenu - menu "MIPS CPU Idle Drivers" depends on MIPS source "drivers/cpuidle/Kconfig.mips" diff --git a/drivers/cpuidle/Kconfig.arm b/drivers/cpuidle/Kconfig.arm index 8e07c9419153..21340e0be73e 100644 --- a/drivers/cpuidle/Kconfig.arm +++ b/drivers/cpuidle/Kconfig.arm @@ -1,10 +1,20 @@ # # ARM CPU Idle drivers # +config ARM_CPUIDLE + bool "Generic ARM/ARM64 CPU idle Driver" + select DT_IDLE_STATES + help + Select this to enable generic cpuidle driver for ARM. + It provides a generic idle driver whose idle states are configured + at run-time through DT nodes. The CPUidle suspend backend is + initialized by calling the CPU operations init idle hook + provided by architecture code. + config ARM_BIG_LITTLE_CPUIDLE bool "Support for ARM big.LITTLE processors" depends on ARCH_VEXPRESS_TC2_PM || ARCH_EXYNOS - depends on MCPM + depends on MCPM && !ARM64 select ARM_CPU_SUSPEND select CPU_IDLE_MULTIPLE_DRIVERS select DT_IDLE_STATES @@ -16,51 +26,51 @@ config ARM_BIG_LITTLE_CPUIDLE config ARM_CLPS711X_CPUIDLE bool "CPU Idle Driver for CLPS711X processors" - depends on ARCH_CLPS711X || COMPILE_TEST + depends on ARCH_CLPS711X && !ARM64 || COMPILE_TEST help Select this to enable cpuidle on Cirrus Logic CLPS711X SOCs. config ARM_HIGHBANK_CPUIDLE bool "CPU Idle Driver for Calxeda processors" - depends on ARM_PSCI + depends on ARM_PSCI && !ARM64 select ARM_CPU_SUSPEND help Select this to enable cpuidle on Calxeda processors. config ARM_KIRKWOOD_CPUIDLE bool "CPU Idle Driver for Marvell Kirkwood SoCs" - depends on MACH_KIRKWOOD + depends on MACH_KIRKWOOD && !ARM64 help This adds the CPU Idle driver for Marvell Kirkwood SoCs. config ARM_ZYNQ_CPUIDLE bool "CPU Idle Driver for Xilinx Zynq processors" - depends on ARCH_ZYNQ + depends on ARCH_ZYNQ && !ARM64 help Select this to enable cpuidle on Xilinx Zynq processors. config ARM_U8500_CPUIDLE bool "Cpu Idle Driver for the ST-E u8500 processors" - depends on ARCH_U8500 + depends on ARCH_U8500 && !ARM64 help Select this to enable cpuidle for ST-E u8500 processors config ARM_AT91_CPUIDLE bool "Cpu Idle Driver for the AT91 processors" default y - depends on ARCH_AT91 + depends on ARCH_AT91 && !ARM64 help Select this to enable cpuidle for AT91 processors config ARM_EXYNOS_CPUIDLE bool "Cpu Idle Driver for the Exynos processors" - depends on ARCH_EXYNOS + depends on ARCH_EXYNOS && !ARM64 select ARCH_NEEDS_CPU_IDLE_COUPLED if SMP help Select this to enable cpuidle for Exynos processors config ARM_MVEBU_V7_CPUIDLE bool "CPU Idle Driver for mvebu v7 family processors" - depends on ARCH_MVEBU + depends on ARCH_MVEBU && !ARM64 help Select this to enable cpuidle on Armada 370, 38x and XP processors. diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64 deleted file mode 100644 index 6effb3656735..000000000000 --- a/drivers/cpuidle/Kconfig.arm64 +++ /dev/null @@ -1,13 +0,0 @@ -# -# ARM64 CPU Idle drivers -# - -config ARM64_CPUIDLE - bool "Generic ARM64 CPU idle Driver" - select DT_IDLE_STATES - help - Select this to enable generic cpuidle driver for ARM64. - It provides a generic idle driver whose idle states are configured - at run-time through DT nodes. The CPUidle suspend backend is - initialized by calling the CPU operations init idle hook - provided by architecture code. diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile index 4d177b916f75..3ba81b1dffad 100644 --- a/drivers/cpuidle/Makefile +++ b/drivers/cpuidle/Makefile @@ -17,16 +17,13 @@ obj-$(CONFIG_ARM_ZYNQ_CPUIDLE) += cpuidle-zynq.o obj-$(CONFIG_ARM_U8500_CPUIDLE) += cpuidle-ux500.o obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o +obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o ############################################################################### # MIPS drivers obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o ############################################################################### -# ARM64 drivers -obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o - -############################################################################### # POWERPC drivers obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm.c index 39a2c62716c3..545069d5fdfb 100644 --- a/drivers/cpuidle/cpuidle-arm64.c +++ b/drivers/cpuidle/cpuidle-arm.c @@ -1,5 +1,5 @@ /* - * ARM64 generic CPU idle driver. + * ARM/ARM64 generic CPU idle driver. * * Copyright (C) 2014 ARM Ltd. * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> @@ -9,7 +9,7 @@ * published by the Free Software Foundation. */ -#define pr_fmt(fmt) "CPUidle arm64: " fmt +#define pr_fmt(fmt) "CPUidle arm: " fmt #include <linux/cpuidle.h> #include <linux/cpumask.h> @@ -17,13 +17,14 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/of.h> +#include <linux/slab.h> #include <asm/cpuidle.h> #include "dt_idle_states.h" /* - * arm64_enter_idle_state - Programs CPU to enter the specified state + * arm_enter_idle_state - Programs CPU to enter the specified state * * dev: cpuidle device * drv: cpuidle driver @@ -32,8 +33,8 @@ * Called from the CPUidle framework to program the device to the * specified target state selected by the governor. */ -static int arm64_enter_idle_state(struct cpuidle_device *dev, - struct cpuidle_driver *drv, int idx) +static int arm_enter_idle_state(struct cpuidle_device *dev, + struct cpuidle_driver *drv, int idx) { int ret; @@ -49,7 +50,7 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev, * call the CPU ops suspend protocol with idle index as a * parameter. */ - ret = cpu_suspend(idx); + arm_cpuidle_suspend(idx); cpu_pm_exit(); } @@ -57,8 +58,8 @@ static int arm64_enter_idle_state(struct cpuidle_device *dev, return ret ? -1 : idx; } -static struct cpuidle_driver arm64_idle_driver = { - .name = "arm64_idle", +static struct cpuidle_driver arm_idle_driver = { + .name = "arm_idle", .owner = THIS_MODULE, /* * State at index 0 is standby wfi and considered standard @@ -68,32 +69,33 @@ static struct cpuidle_driver arm64_idle_driver = { * handler for idle state index 0. */ .states[0] = { - .enter = arm64_enter_idle_state, + .enter = arm_enter_idle_state, .exit_latency = 1, .target_residency = 1, .power_usage = UINT_MAX, .name = "WFI", - .desc = "ARM64 WFI", + .desc = "ARM WFI", } }; -static const struct of_device_id arm64_idle_state_match[] __initconst = { +static const struct of_device_id arm_idle_state_match[] __initconst = { { .compatible = "arm,idle-state", - .data = arm64_enter_idle_state }, + .data = arm_enter_idle_state }, { }, }; /* - * arm64_idle_init + * arm_idle_init * - * Registers the arm64 specific cpuidle driver with the cpuidle + * Registers the arm 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) +static int __init arm_idle_init(void) { int cpu, ret; - struct cpuidle_driver *drv = &arm64_idle_driver; + struct cpuidle_driver *drv = &arm_idle_driver; + struct cpuidle_device *dev; /* * Initialize idle states data, starting at index 1. @@ -101,22 +103,61 @@ static int __init arm64_idle_init(void) * let the driver initialization fail accordingly since there is no * reason to initialize the idle driver if only wfi is supported. */ - ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1); + ret = dt_init_idle_driver(drv, arm_idle_state_match, 1); if (ret <= 0) return ret ? : -ENODEV; + ret = cpuidle_register_driver(drv); + if (ret) { + pr_err("Failed to register cpuidle driver\n"); + return ret; + } + /* * Call arch CPU operations in order to initialize * idle states suspend back-end specific data */ for_each_possible_cpu(cpu) { - ret = cpu_init_idle(cpu); + ret = arm_cpuidle_init(cpu); + + /* + * Skip the cpuidle device initialization if the reported + * failure is a HW misconfiguration/breakage (-ENXIO). + */ + if (ret == -ENXIO) + continue; + if (ret) { pr_err("CPU %d failed to init idle CPU ops\n", cpu); - return ret; + goto out_fail; + } + + dev = kzalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) { + pr_err("Failed to allocate cpuidle device\n"); + goto out_fail; + } + dev->cpu = cpu; + + ret = cpuidle_register_device(dev); + if (ret) { + pr_err("Failed to register cpuidle device for CPU %d\n", + cpu); + kfree(dev); + goto out_fail; } } - return cpuidle_register(drv, NULL); + 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; } -device_initcall(arm64_idle_init); +device_initcall(arm_idle_init); diff --git a/drivers/cpuidle/cpuidle-at91.c b/drivers/cpuidle/cpuidle-at91.c index aae7bfc1ea36..f2446c78d87c 100644 --- a/drivers/cpuidle/cpuidle-at91.c +++ b/drivers/cpuidle/cpuidle-at91.c @@ -19,7 +19,6 @@ #include <linux/cpuidle.h> #include <linux/io.h> #include <linux/export.h> -#include <asm/proc-fns.h> #include <asm/cpuidle.h> #define AT91_MAX_STATES 2 diff --git a/drivers/cpuidle/cpuidle-exynos.c b/drivers/cpuidle/cpuidle-exynos.c index 26f5f29fdb03..0c06ea2f50bb 100644 --- a/drivers/cpuidle/cpuidle-exynos.c +++ b/drivers/cpuidle/cpuidle-exynos.c @@ -19,7 +19,6 @@ #include <linux/of.h> #include <linux/platform_data/cpuidle-exynos.h> -#include <asm/proc-fns.h> #include <asm/suspend.h> #include <asm/cpuidle.h> diff --git a/drivers/cpuidle/cpuidle-kirkwood.c b/drivers/cpuidle/cpuidle-kirkwood.c index cea0a6c4b1db..d23d8f468c12 100644 --- a/drivers/cpuidle/cpuidle-kirkwood.c +++ b/drivers/cpuidle/cpuidle-kirkwood.c @@ -21,7 +21,6 @@ #include <linux/cpuidle.h> #include <linux/io.h> #include <linux/export.h> -#include <asm/proc-fns.h> #include <asm/cpuidle.h> #define KIRKWOOD_MAX_STATES 2 diff --git a/drivers/cpuidle/cpuidle-ux500.c b/drivers/cpuidle/cpuidle-ux500.c index 66f81e410f0d..8bf895c0017d 100644 --- a/drivers/cpuidle/cpuidle-ux500.c +++ b/drivers/cpuidle/cpuidle-ux500.c @@ -19,7 +19,6 @@ #include <linux/platform_device.h> #include <asm/cpuidle.h> -#include <asm/proc-fns.h> static atomic_t master = ATOMIC_INIT(0); static DEFINE_SPINLOCK(master_lock); diff --git a/drivers/cpuidle/cpuidle-zynq.c b/drivers/cpuidle/cpuidle-zynq.c index 002b8c9f98f5..543292b1d38e 100644 --- a/drivers/cpuidle/cpuidle-zynq.c +++ b/drivers/cpuidle/cpuidle-zynq.c @@ -28,7 +28,6 @@ #include <linux/init.h> #include <linux/cpuidle.h> #include <linux/platform_device.h> -#include <asm/proc-fns.h> #include <asm/cpuidle.h> #define ZYNQ_MAX_STATES 2 diff --git a/include/asm-generic/vmlinux.lds.h b/include/asm-generic/vmlinux.lds.h index ac78910d7416..91c09305106d 100644 --- a/include/asm-generic/vmlinux.lds.h +++ b/include/asm-generic/vmlinux.lds.h @@ -167,6 +167,7 @@ #define IOMMU_OF_TABLES() OF_TABLE(CONFIG_OF_IOMMU, iommu) #define RESERVEDMEM_OF_TABLES() OF_TABLE(CONFIG_OF_RESERVED_MEM, reservedmem) #define CPU_METHOD_OF_TABLES() OF_TABLE(CONFIG_SMP, cpu_method) +#define CPUIDLE_METHOD_OF_TABLES() OF_TABLE(CONFIG_CPU_IDLE, cpuidle_method) #define EARLYCON_OF_TABLES() OF_TABLE(CONFIG_SERIAL_EARLYCON, earlycon) #define KERNEL_DTB() \ @@ -501,6 +502,7 @@ CLKSRC_OF_TABLES() \ IOMMU_OF_TABLES() \ CPU_METHOD_OF_TABLES() \ + CPUIDLE_METHOD_OF_TABLES() \ KERNEL_DTB() \ IRQCHIP_OF_MATCH_TABLE() \ EARLYCON_OF_TABLES() |