[PATCH] Exynos4: Basic CPUidle support

Kyungmin Park kmpark at infradead.org
Thu Mar 3 23:54:55 EST 2011


From: Kyungmin Park <kyungmin.park at samsung.com>

This series adds Exynos4 cpuidle support.
In cpuidle, low power states are attempted.
Currently only WFI is supported.

Signed-off-by: Kyungmin Park <kyungmin.park at samsung.com>
---
diff --git a/arch/arm/mach-exynos4/Makefile b/arch/arm/mach-exynos4/Makefile
index fa9e814..ba5d5b7 100644
--- a/arch/arm/mach-exynos4/Makefile
+++ b/arch/arm/mach-exynos4/Makefile
@@ -15,6 +15,7 @@ obj-				:=
 obj-$(CONFIG_CPU_EXYNOS4210)	+= cpu.o init.o clock.o irq-combiner.o
 obj-$(CONFIG_CPU_EXYNOS4210)	+= setup-i2c0.o time.o gpiolib.o irq-eint.o dma.o
 obj-$(CONFIG_CPU_FREQ)		+= cpufreq.o
+obj-$(CONFIG_CPU_IDLE)		+= cpuidle.o
 
 obj-$(CONFIG_SMP)		+= platsmp.o headsmp.o
 obj-$(CONFIG_LOCAL_TIMERS)	+= localtimer.o
diff --git a/arch/arm/mach-exynos4/cpuidle.c b/arch/arm/mach-exynos4/cpuidle.c
new file mode 100644
index 0000000..eb2f945
--- /dev/null
+++ b/arch/arm/mach-exynos4/cpuidle.c
@@ -0,0 +1,110 @@
+/*
+ * arch/arm/mach-exynos4/cpuidle.c
+ *
+ * This file is licensed under the terms of the GNU General Public
+ * License version 2.  This program is licensed "as is" without any
+ * warranty of any kind, whether express or implied.
+ */
+
+#include <linux/io.h>
+#include <linux/cpuidle.h>
+
+#include <asm/proc-fns.h>
+
+#include <mach/hardware.h>
+
+#define EXYNOS4_MAX_STATES	1
+/* C1 - CPUx WFI */
+#define EXYNOS4_STATE_C1	0
+
+struct exynos4_processor_cx {
+	unsigned int	flags;
+	unsigned int	exit_latency; /* in US */
+	unsigned int	target_residency; /* in US */
+	const char	*desc;
+};
+
+struct exynos4_processor_cx exynos4_power_states[EXYNOS4_MAX_STATES];
+
+static int exynos4_enter_idle(struct cpuidle_device *dev,
+				struct cpuidle_state *state)
+{
+	struct timespec ts_preidle, ts_postidle, ts_idle;
+
+	/* Used to keep track of the total time in idle */
+	getnstimeofday(&ts_preidle);
+
+	local_irq_disable();
+	local_fiq_disable();
+
+	cpu_do_idle();
+
+	getnstimeofday(&ts_postidle);
+	ts_idle = timespec_sub(ts_postidle, ts_preidle);
+
+	local_irq_enable();
+	local_fiq_enable();
+
+	return ts_idle.tv_nsec / NSEC_PER_USEC + ts_idle.tv_sec * USEC_PER_SEC;
+}
+
+static void exynos4_init_power_states(void)
+{
+	/* C1 CPUx WFI */
+	exynos4_power_states[EXYNOS4_STATE_C1].exit_latency = 1;
+	exynos4_power_states[EXYNOS4_STATE_C1].target_residency = 10000;
+	exynos4_power_states[EXYNOS4_STATE_C1].flags = CPUIDLE_FLAG_TIME_VALID;
+	exynos4_power_states[EXYNOS4_STATE_C1].desc = "CPUx WFI";
+}
+
+static DEFINE_PER_CPU(struct cpuidle_device, exynos_cpuidle_device);
+
+static struct cpuidle_driver exynos4_cpuidle_driver = {
+	.name		= "exynos4_idle",
+	.owner		= THIS_MODULE,
+};
+
+static int __init exynos4_cpuidle_init(void)
+{
+	struct exynos4_processor_cx *cx;
+	struct cpuidle_state *state;
+	struct cpuidle_device *dev;
+	int cpu_id, i, count;
+
+	exynos4_init_power_states();
+	cpuidle_register_driver(&exynos4_cpuidle_driver);
+
+	for_each_cpu(cpu_id, cpu_online_mask) {
+		pr_info("cpuidle for CPU%d registered\n", cpu_id);
+		dev = &per_cpu(exynos_cpuidle_device, cpu_id);
+		dev->cpu = cpu_id;
+		count = 0;
+
+		for (i = EXYNOS4_STATE_C1; i < EXYNOS4_MAX_STATES; i++) {
+			cx = &exynos4_power_states[i];
+			state = &dev->states[count];
+
+			cpuidle_set_statedata(state, cx);
+			state->exit_latency = cx->exit_latency;
+			state->target_residency = cx->target_residency;
+			state->flags = cx->flags;
+			state->enter = exynos4_enter_idle;
+			sprintf(state->name, "C%d", count+1);
+			strncpy(state->desc, cx->desc, CPUIDLE_DESC_LEN);
+			count++;
+		}
+
+		if (!count)
+			return -EINVAL;
+		dev->state_count = count;
+
+		if (cpuidle_register_device(dev)) {
+			pr_err("cpuidle register device failed\n");
+			return -EIO;
+		}
+	}
+
+	return 0;
+}
+
+late_initcall(exynos4_cpuidle_init);



More information about the linux-arm-kernel mailing list