2006-06-20 22:30:19 +04:00
/*
* arch / arm / mach - at91rm9200 / pm . c
* AT91 Power Management
*
* Copyright ( C ) 2005 David Brownell
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*/
# include <linux/pm.h>
# include <linux/sched.h>
# include <linux/proc_fs.h>
# include <linux/pm.h>
# include <linux/interrupt.h>
# include <linux/sysfs.h>
# include <linux/module.h>
# include <linux/platform_device.h>
# include <asm/io.h>
# include <asm/irq.h>
# include <asm/atomic.h>
# include <asm/mach/time.h>
# include <asm/mach/irq.h>
# include <asm/mach-types.h>
# include <asm/arch/gpio.h>
# include "generic.h"
static int at91_pm_valid_state ( suspend_state_t state )
{
switch ( state ) {
case PM_SUSPEND_ON :
case PM_SUSPEND_STANDBY :
case PM_SUSPEND_MEM :
return 1 ;
default :
return 0 ;
}
}
static suspend_state_t target_state ;
/*
* Called after processes are frozen , but before we shutdown devices .
*/
static int at91_pm_prepare ( suspend_state_t state )
{
target_state = state ;
return 0 ;
}
/*
* Verify that all the clocks are correct before entering
* slow - clock mode .
*/
static int at91_pm_verify_clocks ( void )
{
unsigned long scsr ;
int i ;
scsr = at91_sys_read ( AT91_PMC_SCSR ) ;
/* USB must not be using PLLB */
if ( ( scsr & ( AT91_PMC_UHP | AT91_PMC_UDP ) ) ! = 0 ) {
pr_debug ( " AT91: PM - Suspend-to-RAM with USB still active \n " ) ;
return 0 ;
}
# ifdef CONFIG_AT91_PROGRAMMABLE_CLOCKS
/* PCK0..PCK3 must be disabled, or configured to use clk32k */
for ( i = 0 ; i < 4 ; i + + ) {
u32 css ;
if ( ( scsr & ( AT91_PMC_PCK0 < < i ) ) = = 0 )
continue ;
css = at91_sys_read ( AT91_PMC_PCKR ( i ) ) & AT91_PMC_CSS ;
if ( css ! = AT91_PMC_CSS_SLOW ) {
pr_debug ( " AT91: PM - Suspend-to-RAM with PCK%d src %d \n " , i , css ) ;
return 0 ;
}
}
# endif
return 1 ;
}
/*
* Call this from platform driver suspend ( ) to see how deeply to suspend .
* For example , some controllers ( like OHCI ) need one of the PLL clocks
* in order to act as a wakeup source , and those are not available when
* going into slow clock mode .
*
* REVISIT : generalize as clk_will_be_available ( clk ) ? Other platforms have
* the very same problem ( but not using at91 main_clk ) , and it ' d be better
* to add one generic API rather than lots of platform - specific ones .
*/
int at91_suspend_entering_slow_clock ( void )
{
return ( target_state = = PM_SUSPEND_MEM ) ;
}
EXPORT_SYMBOL ( at91_suspend_entering_slow_clock ) ;
static void ( * slow_clock ) ( void ) ;
static int at91_pm_enter ( suspend_state_t state )
{
at91_gpio_suspend ( ) ;
at91_irq_suspend ( ) ;
pr_debug ( " AT91: PM - wake mask %08x, pm state %d \n " ,
/* remember all the always-wake irqs */
( at91_sys_read ( AT91_PMC_PCSR )
| ( 1 < < AT91_ID_FIQ )
| ( 1 < < AT91_ID_SYS )
2006-09-27 12:44:11 +04:00
| ( 1 < < AT91RM9200_ID_IRQ0 )
| ( 1 < < AT91RM9200_ID_IRQ1 )
| ( 1 < < AT91RM9200_ID_IRQ2 )
| ( 1 < < AT91RM9200_ID_IRQ3 )
| ( 1 < < AT91RM9200_ID_IRQ4 )
| ( 1 < < AT91RM9200_ID_IRQ5 )
| ( 1 < < AT91RM9200_ID_IRQ6 ) )
2006-06-20 22:30:19 +04:00
& at91_sys_read ( AT91_AIC_IMR ) ,
state ) ;
switch ( state ) {
/*
* Suspend - to - RAM is like STANDBY plus slow clock mode , so
* drivers must suspend more deeply : only the master clock
* controller may be using the main oscillator .
*/
case PM_SUSPEND_MEM :
/*
* Ensure that clocks are in a valid state .
*/
if ( ! at91_pm_verify_clocks ( ) )
goto error ;
/*
* Enter slow clock mode by switching over to clk32k and
* turning off the main oscillator ; reverse on wakeup .
*/
if ( slow_clock ) {
slow_clock ( ) ;
break ;
} else {
/* DEVELOPMENT ONLY */
pr_info ( " AT91: PM - no slow clock mode yet ... \n " ) ;
/* FALLTHROUGH leaving master clock alone */
}
/*
* STANDBY mode has * all * drivers suspended ; ignores irqs not
* marked as ' wakeup ' event sources ; and reduces DRAM power .
* But otherwise it ' s identical to PM_SUSPEND_ON : cpu idle , and
* nothing fancy done with main or cpu clocks .
*/
case PM_SUSPEND_STANDBY :
/*
* NOTE : the Wait - for - Interrupt instruction needs to be
* in icache so the SDRAM stays in self - refresh mode until
* the wakeup IRQ occurs .
*/
asm ( " b 1f; .align 5; 1: " ) ;
asm ( " mcr p15, 0, r0, c7, c10, 4 " ) ; /* drain write buffer */
at91_sys_write ( AT91_SDRAMC_SRR , 1 ) ; /* self-refresh mode */
/* fall though to next state */
case PM_SUSPEND_ON :
asm ( " mcr p15, 0, r0, c7, c0, 4 " ) ; /* wait for interrupt */
break ;
default :
pr_debug ( " AT91: PM - bogus suspend state %d \n " , state ) ;
goto error ;
}
pr_debug ( " AT91: PM - wakeup %08x \n " ,
at91_sys_read ( AT91_AIC_IPR ) & at91_sys_read ( AT91_AIC_IMR ) ) ;
error :
target_state = PM_SUSPEND_ON ;
at91_irq_resume ( ) ;
at91_gpio_resume ( ) ;
return 0 ;
}
static struct pm_ops at91_pm_ops = {
. pm_disk_mode = 0 ,
. valid = at91_pm_valid_state ,
. prepare = at91_pm_prepare ,
. enter = at91_pm_enter ,
} ;
static int __init at91_pm_init ( void )
{
printk ( " AT91: Power Management \n " ) ;
# ifdef CONFIG_AT91_PM_SLOW_CLOCK
/* REVISIT allocations of SRAM should be dynamically managed.
* FIQ handlers and other components will want SRAM / TCM too . . .
*/
slow_clock = ( void * ) ( AT91_VA_BASE_SRAM + ( 3 * SZ_4K ) ) ;
memcpy ( slow_clock , at91rm9200_slow_clock , at91rm9200_slow_clock_sz ) ;
# endif
/* Disable SDRAM low-power mode. Cannot be used with self-refresh. */
at91_sys_write ( AT91_SDRAMC_LPR , 0 ) ;
pm_set_ops ( & at91_pm_ops ) ;
return 0 ;
}
arch_initcall ( at91_pm_init ) ;