aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorLorenzo Pieralisi <lorenzo.pieralisi@arm.com>2014-02-28 08:03:44 -0500
committerDaniel Lezcano <daniel.lezcano@linaro.org>2014-09-25 04:52:21 -0400
commit3299b63de384159579143d4abdfb94013e0b5470 (patch)
tree8e5e436fbfe85821d35881cb64aa8cdcc391bedc
parent9f14da345599c14b329cf5ac9499ad322056dd32 (diff)
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. The driver relies on the arm64 CPU operations to call the idle initialization hook used to parse and save suspend back-end specific idle states information upon probing. Idle state index 0 is always initialized as a simple wfi state, ie always considered present and functional on all ARM64 platforms. Idle state indices higher than 0 trigger idle state entry by calling the cpu_suspend function, that triggers the suspend operation through the CPU operations suspend back-end hook. cpu_suspend passes the idle state index as a parameter so that the CPU operations suspend back-end can retrieve the required idle state data by using the idle state index to execute a look-up on its internal data structures. Reviewed-by: Ashwin Chaugule <ashwin.chaugule@linaro.org> Reviewed-by: Catalin Marinas <catalin.marinas@arm.com> Signed-off-by: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com> Signed-off-by: Daniel Lezcano <daniel.lezcano@linaro.org>
-rw-r--r--drivers/cpuidle/Kconfig5
-rw-r--r--drivers/cpuidle/Kconfig.arm6414
-rw-r--r--drivers/cpuidle/Makefile4
-rw-r--r--drivers/cpuidle/cpuidle-arm64.c133
4 files changed, 156 insertions, 0 deletions
diff --git a/drivers/cpuidle/Kconfig b/drivers/cpuidle/Kconfig
index 8deb934c6356..c5029c1209b4 100644
--- a/drivers/cpuidle/Kconfig
+++ b/drivers/cpuidle/Kconfig
@@ -33,6 +33,11 @@ depends on ARM
33source "drivers/cpuidle/Kconfig.arm" 33source "drivers/cpuidle/Kconfig.arm"
34endmenu 34endmenu
35 35
36menu "ARM64 CPU Idle Drivers"
37depends on ARM64
38source "drivers/cpuidle/Kconfig.arm64"
39endmenu
40
36menu "MIPS CPU Idle Drivers" 41menu "MIPS CPU Idle Drivers"
37depends on MIPS 42depends on MIPS
38source "drivers/cpuidle/Kconfig.mips" 43source "drivers/cpuidle/Kconfig.mips"
diff --git a/drivers/cpuidle/Kconfig.arm64 b/drivers/cpuidle/Kconfig.arm64
new file mode 100644
index 000000000000..d0a08ed1b2ee
--- /dev/null
+++ b/drivers/cpuidle/Kconfig.arm64
@@ -0,0 +1,14 @@
1#
2# ARM64 CPU Idle drivers
3#
4
5config ARM64_CPUIDLE
6 bool "Generic ARM64 CPU idle Driver"
7 select ARM64_CPU_SUSPEND
8 select DT_IDLE_STATES
9 help
10 Select this to enable generic cpuidle driver for ARM64.
11 It provides a generic idle driver whose idle states are configured
12 at run-time through DT nodes. The CPUidle suspend backend is
13 initialized by calling the CPU operations init idle hook
14 provided by architecture code.
diff --git a/drivers/cpuidle/Makefile b/drivers/cpuidle/Makefile
index 002b65336ba9..4d177b916f75 100644
--- a/drivers/cpuidle/Makefile
+++ b/drivers/cpuidle/Makefile
@@ -23,6 +23,10 @@ obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
23obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o 23obj-$(CONFIG_MIPS_CPS_CPUIDLE) += cpuidle-cps.o
24 24
25############################################################################### 25###############################################################################
26# ARM64 drivers
27obj-$(CONFIG_ARM64_CPUIDLE) += cpuidle-arm64.o
28
29###############################################################################
26# POWERPC drivers 30# POWERPC drivers
27obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o 31obj-$(CONFIG_PSERIES_CPUIDLE) += cpuidle-pseries.o
28obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o 32obj-$(CONFIG_POWERNV_CPUIDLE) += cpuidle-powernv.o
diff --git a/drivers/cpuidle/cpuidle-arm64.c b/drivers/cpuidle/cpuidle-arm64.c
new file mode 100644
index 000000000000..50997ea942fc
--- /dev/null
+++ b/drivers/cpuidle/cpuidle-arm64.c
@@ -0,0 +1,133 @@
1/*
2 * ARM64 generic CPU idle driver.
3 *
4 * Copyright (C) 2014 ARM Ltd.
5 * Author: Lorenzo Pieralisi <lorenzo.pieralisi@arm.com>
6 *
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.
10 */
11
12#define pr_fmt(fmt) "CPUidle arm64: " fmt
13
14#include <linux/cpuidle.h>
15#include <linux/cpumask.h>
16#include <linux/cpu_pm.h>
17#include <linux/kernel.h>
18#include <linux/module.h>
19#include <linux/of.h>
20
21#include <asm/cpuidle.h>
22#include <asm/suspend.h>
23
24#include "dt_idle_states.h"
25
26/*
27 * arm64_enter_idle_state - Programs CPU to enter the specified state
28 *
29 * dev: cpuidle device
30 * drv: cpuidle driver
31 * idx: state index
32 *
33 * Called from the CPUidle framework to program the device to the
34 * specified target state selected by the governor.
35 */
36static int arm64_enter_idle_state(struct cpuidle_device *dev,
37 struct cpuidle_driver *drv, int idx)
38{
39 int ret;
40
41 if (!idx) {
42 cpu_do_idle();
43 return idx;
44 }
45
46 ret = cpu_pm_enter();
47 if (!ret) {
48 /*
49 * Pass idle state index to cpu_suspend which in turn will
50 * call the CPU ops suspend protocol with idle index as a
51 * parameter.
52 */
53 ret = cpu_suspend(idx);
54
55 cpu_pm_exit();
56 }
57
58 return ret ? -1 : idx;
59}
60
61static struct cpuidle_driver arm64_idle_driver = {
62 .name = "arm64_idle",
63 .owner = THIS_MODULE,
64 /*
65 * State at index 0 is standby wfi and considered standard
66 * on all ARM platforms. If in some platforms simple wfi
67 * can't be used as "state 0", DT bindings must be implemented
68 * to work around this issue and allow installing a special
69 * handler for idle state index 0.
70 */
71 .states[0] = {
72 .enter = arm64_enter_idle_state,
73 .exit_latency = 1,
74 .target_residency = 1,
75 .power_usage = UINT_MAX,
76 .flags = CPUIDLE_FLAG_TIME_VALID,
77 .name = "WFI",
78 .desc = "ARM64 WFI",
79 }
80};
81
82static const struct of_device_id arm64_idle_state_match[] __initconst = {
83 { .compatible = "arm,idle-state",
84 .data = arm64_enter_idle_state },
85 { },
86};
87
88/*
89 * arm64_idle_init
90 *
91 * Registers the arm64 specific cpuidle driver with the cpuidle
92 * framework. It relies on core code to parse the idle states
93 * and initialize them using driver data structures accordingly.
94 */
95static int __init arm64_idle_init(void)
96{
97 int cpu, ret;
98 struct cpuidle_driver *drv = &arm64_idle_driver;
99
100 /*
101 * Initialize idle states data, starting at index 1.
102 * This driver is DT only, if no DT idle states are detected (ret == 0)
103 * let the driver initialization fail accordingly since there is no
104 * reason to initialize the idle driver if only wfi is supported.
105 */
106 ret = dt_init_idle_driver(drv, arm64_idle_state_match, 1);
107 if (ret <= 0) {
108 if (ret)
109 pr_err("failed to initialize idle states\n");
110 return ret ? : -ENODEV;
111 }
112
113 /*
114 * Call arch CPU operations in order to initialize
115 * idle states suspend back-end specific data
116 */
117 for_each_possible_cpu(cpu) {
118 ret = cpu_init_idle(cpu);
119 if (ret) {
120 pr_err("CPU %d failed to init idle CPU ops\n", cpu);
121 return ret;
122 }
123 }
124
125 ret = cpuidle_register(drv, NULL);
126 if (ret) {
127 pr_err("failed to register cpuidle driver\n");
128 return ret;
129 }
130
131 return 0;
132}
133device_initcall(arm64_idle_init);