ARM: 5778/1: AT91: Add cpuidle support
authorAlbin Tonnerre <albin.tonnerre@free-electrons.com>
Sun, 1 Nov 2009 17:40:50 +0000 (18:40 +0100)
committerRussell King <rmk+kernel@arm.linux.org.uk>
Tue, 10 Nov 2009 23:46:35 +0000 (23:46 +0000)
This patch adds the support for cpuidle on AT91 SoCs, taken from the
cpuidle support in mach-kirkwood.
cpuidle needs sdram_selfrefresh_enable and _disable, so move their
definition to a separate header file instead of duplicating the code
already used in pm.c.

Tested-by: Nicolas Ferre <nicolas.ferre@atmel.com>
Signed-off-by: Albin Tonnerre <albin.tonnerre@free-electrons.com>
Acked-by: Andrew Victor <linux@maxim.org.za>
Signed-off-by: Russell King <rmk+kernel@arm.linux.org.uk>
arch/arm/mach-at91/Makefile
arch/arm/mach-at91/cpuidle.c [new file with mode: 0644]
arch/arm/mach-at91/pm.c
arch/arm/mach-at91/pm.h [new file with mode: 0644]

index ada440a..f9e12bb 100644 (file)
@@ -77,6 +77,7 @@ obj-y                         += leds.o
 # Power Management
 obj-$(CONFIG_PM)               += pm.o
 obj-$(CONFIG_AT91_SLOW_CLOCK)  += pm_slowclock.o
+obj-$(CONFIG_CPU_IDLE) += cpuidle.o
 
 ifeq ($(CONFIG_PM_DEBUG),y)
 CFLAGS_pm.o += -DDEBUG
diff --git a/arch/arm/mach-at91/cpuidle.c b/arch/arm/mach-at91/cpuidle.c
new file mode 100644 (file)
index 0000000..1cfeac1
--- /dev/null
@@ -0,0 +1,94 @@
+/*
+ * based on arch/arm/mach-kirkwood/cpuidle.c
+ *
+ * CPU idle support for AT91 SoC
+ *
+ * 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.
+ *
+ * The cpu idle uses wait-for-interrupt and RAM self refresh in order
+ * to implement two idle states -
+ * #1 wait-for-interrupt
+ * #2 wait-for-interrupt and RAM self refresh
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/cpuidle.h>
+#include <asm/proc-fns.h>
+#include <linux/io.h>
+
+#include "pm.h"
+
+#define AT91_MAX_STATES        2
+
+static DEFINE_PER_CPU(struct cpuidle_device, at91_cpuidle_device);
+
+static struct cpuidle_driver at91_idle_driver = {
+       .name =         "at91_idle",
+       .owner =        THIS_MODULE,
+};
+
+/* Actual code that puts the SoC in different idle states */
+static int at91_enter_idle(struct cpuidle_device *dev,
+                              struct cpuidle_state *state)
+{
+       struct timeval before, after;
+       int idle_time;
+       u32 saved_lpr;
+
+       local_irq_disable();
+       do_gettimeofday(&before);
+       if (state == &dev->states[0])
+               /* Wait for interrupt state */
+               cpu_do_idle();
+       else if (state == &dev->states[1]) {
+               asm("b 1f; .align 5; 1:");
+               asm("mcr p15, 0, r0, c7, c10, 4");      /* drain write buffer */
+               saved_lpr = sdram_selfrefresh_enable();
+               cpu_do_idle();
+               sdram_selfrefresh_disable(saved_lpr);
+       }
+       do_gettimeofday(&after);
+       local_irq_enable();
+       idle_time = (after.tv_sec - before.tv_sec) * USEC_PER_SEC +
+                       (after.tv_usec - before.tv_usec);
+       return idle_time;
+}
+
+/* Initialize CPU idle by registering the idle states */
+static int at91_init_cpuidle(void)
+{
+       struct cpuidle_device *device;
+
+       cpuidle_register_driver(&at91_idle_driver);
+
+       device = &per_cpu(at91_cpuidle_device, smp_processor_id());
+       device->state_count = AT91_MAX_STATES;
+
+       /* Wait for interrupt state */
+       device->states[0].enter = at91_enter_idle;
+       device->states[0].exit_latency = 1;
+       device->states[0].target_residency = 10000;
+       device->states[0].flags = CPUIDLE_FLAG_TIME_VALID;
+       strcpy(device->states[0].name, "WFI");
+       strcpy(device->states[0].desc, "Wait for interrupt");
+
+       /* Wait for interrupt and RAM self refresh state */
+       device->states[1].enter = at91_enter_idle;
+       device->states[1].exit_latency = 10;
+       device->states[1].target_residency = 10000;
+       device->states[1].flags = CPUIDLE_FLAG_TIME_VALID;
+       strcpy(device->states[1].name, "RAM_SR");
+       strcpy(device->states[1].desc, "WFI and RAM Self Refresh");
+
+       if (cpuidle_register_device(device)) {
+               printk(KERN_ERR "at91_init_cpuidle: Failed registering\n");
+               return -EIO;
+       }
+       return 0;
+}
+
+device_initcall(at91_init_cpuidle);
index 4028724..6156689 100644 (file)
 #include <mach/cpu.h>
 
 #include "generic.h"
-
-#ifdef CONFIG_ARCH_AT91RM9200
-#include <mach/at91rm9200_mc.h>
-
-/*
- * The AT91RM9200 goes into self-refresh mode with this command, and will
- * terminate self-refresh automatically on the next SDRAM access.
- */
-#define sdram_selfrefresh_enable()     at91_sys_write(AT91_SDRAMC_SRR, 1)
-#define sdram_selfrefresh_disable()    do {} while (0)
-
-#elif defined(CONFIG_ARCH_AT91CAP9)
-#include <mach/at91cap9_ddrsdr.h>
-
-static u32 saved_lpr;
-
-static inline void sdram_selfrefresh_enable(void)
-{
-       u32 lpr;
-
-       saved_lpr = at91_sys_read(AT91_DDRSDRC_LPR);
-
-       lpr = saved_lpr & ~AT91_DDRSDRC_LPCB;
-       at91_sys_write(AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH);
-}
-
-#define sdram_selfrefresh_disable()    at91_sys_write(AT91_DDRSDRC_LPR, saved_lpr)
-
-#else
-#include <mach/at91sam9_sdramc.h>
-
-#ifdef CONFIG_ARCH_AT91SAM9263
-/*
- * FIXME either or both the SDRAM controllers (EB0, EB1) might be in use;
- * handle those cases both here and in the Suspend-To-RAM support.
- */
-#define        AT91_SDRAMC     AT91_SDRAMC0
-#warning Assuming EB1 SDRAM controller is *NOT* used
-#endif
-
-static u32 saved_lpr;
-
-static inline void sdram_selfrefresh_enable(void)
-{
-       u32 lpr;
-
-       saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
-
-       lpr = saved_lpr & ~AT91_SDRAMC_LPCB;
-       at91_sys_write(AT91_SDRAMC_LPR, lpr | AT91_SDRAMC_LPCB_SELF_REFRESH);
-}
-
-#define sdram_selfrefresh_disable()    at91_sys_write(AT91_SDRAMC_LPR, saved_lpr)
-
-#endif
-
+#include "pm.h"
 
 /*
  * Show the reason for the previous system reset.
@@ -260,6 +205,7 @@ extern u32 at91_slow_clock_sz;
 
 static int at91_pm_enter(suspend_state_t state)
 {
+       u32 saved_lpr;
        at91_gpio_suspend();
        at91_irq_suspend();
 
@@ -315,9 +261,9 @@ static int at91_pm_enter(suspend_state_t state)
                         */
                        asm("b 1f; .align 5; 1:");
                        asm("mcr p15, 0, r0, c7, c10, 4");      /* drain write buffer */
-                       sdram_selfrefresh_enable();
+                       saved_lpr = sdram_selfrefresh_enable();
                        asm("mcr p15, 0, r0, c7, c0, 4");       /* wait for interrupt */
-                       sdram_selfrefresh_disable();
+                       sdram_selfrefresh_disable(saved_lpr);
                        break;
 
                case PM_SUSPEND_ON:
diff --git a/arch/arm/mach-at91/pm.h b/arch/arm/mach-at91/pm.h
new file mode 100644 (file)
index 0000000..08322c4
--- /dev/null
@@ -0,0 +1,67 @@
+#ifdef CONFIG_ARCH_AT91RM9200
+#include <mach/at91rm9200_mc.h>
+
+/*
+ * The AT91RM9200 goes into self-refresh mode with this command, and will
+ * terminate self-refresh automatically on the next SDRAM access.
+ *
+ * Self-refresh mode is exited as soon as a memory access is made, but we don't
+ * know for sure when that happens. However, we need to restore the low-power
+ * mode if it was enabled before going idle. Restoring low-power mode while
+ * still in self-refresh is "not recommended", but seems to work.
+ */
+
+static inline u32 sdram_selfrefresh_enable(void)
+{
+       u32 saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
+
+       at91_sys_write(AT91_SDRAMC_LPR, 0);
+       at91_sys_write(AT91_SDRAMC_SRR, 1);
+       return saved_lpr;
+}
+
+#define sdram_selfrefresh_disable(saved_lpr)   at91_sys_write(AT91_SDRAMC_LPR, saved_lpr)
+
+#elif defined(CONFIG_ARCH_AT91CAP9)
+#include <mach/at91cap9_ddrsdr.h>
+
+
+static inline u32 sdram_selfrefresh_enable(void)
+{
+       u32 saved_lpr, lpr;
+
+       saved_lpr = at91_sys_read(AT91_DDRSDRC_LPR);
+
+       lpr = saved_lpr & ~AT91_DDRSDRC_LPCB;
+       at91_sys_write(AT91_DDRSDRC_LPR, lpr | AT91_DDRSDRC_LPCB_SELF_REFRESH);
+       return saved_lpr;
+}
+
+#define sdram_selfrefresh_disable(saved_lpr)   at91_sys_write(AT91_DDRSDRC_LPR, saved_lpr)
+
+#else
+#include <mach/at91sam9_sdramc.h>
+
+#ifdef CONFIG_ARCH_AT91SAM9263
+/*
+ * FIXME either or both the SDRAM controllers (EB0, EB1) might be in use;
+ * handle those cases both here and in the Suspend-To-RAM support.
+ */
+#define        AT91_SDRAMC     AT91_SDRAMC0
+#warning Assuming EB1 SDRAM controller is *NOT* used
+#endif
+
+static inline u32 sdram_selfrefresh_enable(void)
+{
+       u32 saved_lpr, lpr;
+
+       saved_lpr = at91_sys_read(AT91_SDRAMC_LPR);
+
+       lpr = saved_lpr & ~AT91_SDRAMC_LPCB;
+       at91_sys_write(AT91_SDRAMC_LPR, lpr | AT91_SDRAMC_LPCB_SELF_REFRESH);
+       return saved_lpr;
+}
+
+#define sdram_selfrefresh_disable(saved_lpr)   at91_sys_write(AT91_SDRAMC_LPR, saved_lpr)
+
+#endif