-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
ARM: 5778/1: AT91: Add cpuidle support
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>
- Loading branch information
Albin Tonnerre
authored and
Russell King
committed
Nov 10, 2009
1 parent
cd3abf9
commit 1ea60cf
Showing
4 changed files
with
166 additions
and
58 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -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 |