2006-06-20 22:30:19 +04:00
/*
2007-02-05 13:42:07 +03:00
* arch / arm / mach - at91 / pm . c
2006-06-20 22:30:19 +04:00
* 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 .
*/
2011-07-26 13:53:52 +04:00
# include <linux/gpio.h>
2007-10-18 14:04:39 +04:00
# include <linux/suspend.h>
2006-06-20 22:30:19 +04:00
# include <linux/sched.h>
# include <linux/proc_fs.h>
# include <linux/interrupt.h>
# include <linux/sysfs.h>
# include <linux/module.h>
# include <linux/platform_device.h>
2008-09-06 15:10:45 +04:00
# include <linux/io.h>
2006-06-20 22:30:19 +04:00
# include <asm/irq.h>
2011-07-27 03:09:06 +04:00
# include <linux/atomic.h>
2006-06-20 22:30:19 +04:00
# include <asm/mach/time.h>
# include <asm/mach/irq.h>
2008-08-05 19:14:15 +04:00
# include <mach/at91_pmc.h>
# include <mach/cpu.h>
2013-11-14 13:49:19 +04:00
# include <mach/hardware.h>
2006-06-20 22:30:19 +04:00
2012-10-30 02:41:28 +04:00
# include "at91_aic.h"
2006-06-20 22:30:19 +04:00
# include "generic.h"
2009-11-01 20:40:50 +03:00
# include "pm.h"
2006-06-20 22:30:19 +04:00
2008-04-03 00:52:19 +04:00
/*
* Show the reason for the previous system reset .
*/
2012-10-30 04:11:24 +04:00
# include "at91_rstc.h"
2012-10-30 04:07:11 +04:00
# include "at91_shdwc.h"
2008-04-03 00:52:19 +04:00
2013-09-23 00:29:57 +04:00
static void ( * at91_pm_standby ) ( void ) ;
2008-04-03 00:52:19 +04:00
static void __init show_reset_status ( void )
{
static char reset [ ] __initdata = " reset " ;
static char general [ ] __initdata = " general " ;
static char wakeup [ ] __initdata = " wakeup " ;
static char watchdog [ ] __initdata = " watchdog " ;
static char software [ ] __initdata = " software " ;
static char user [ ] __initdata = " user " ;
static char unknown [ ] __initdata = " unknown " ;
static char signal [ ] __initdata = " signal " ;
static char rtc [ ] __initdata = " rtc " ;
static char rtt [ ] __initdata = " rtt " ;
static char restore [ ] __initdata = " power-restored " ;
char * reason , * r2 = reset ;
u32 reset_type , wake_type ;
2011-11-17 21:25:52 +04:00
if ( ! at91_shdwc_base | | ! at91_rstc_base )
2011-10-31 21:23:20 +04:00
return ;
2011-11-17 21:25:52 +04:00
reset_type = at91_rstc_read ( AT91_RSTC_SR ) & AT91_RSTC_RSTTYP ;
2011-10-31 21:23:20 +04:00
wake_type = at91_shdwc_read ( AT91_SHDW_SR ) ;
2008-04-03 00:52:19 +04:00
switch ( reset_type ) {
case AT91_RSTC_RSTTYP_GENERAL :
reason = general ;
break ;
case AT91_RSTC_RSTTYP_WAKEUP :
/* board-specific code enabled the wakeup sources */
reason = wakeup ;
/* "wakeup signal" */
if ( wake_type & AT91_SHDW_WAKEUP0 )
r2 = signal ;
else {
r2 = reason ;
if ( wake_type & AT91_SHDW_RTTWK ) /* rtt wakeup */
reason = rtt ;
else if ( wake_type & AT91_SHDW_RTCWK ) /* rtc wakeup */
reason = rtc ;
else if ( wake_type = = 0 ) /* power-restored wakeup */
reason = restore ;
else /* unknown wakeup */
reason = unknown ;
}
break ;
case AT91_RSTC_RSTTYP_WATCHDOG :
reason = watchdog ;
break ;
case AT91_RSTC_RSTTYP_SOFTWARE :
reason = software ;
break ;
case AT91_RSTC_RSTTYP_USER :
reason = user ;
break ;
default :
reason = unknown ;
break ;
}
pr_info ( " AT91: Starting after %s %s \n " , reason , r2 ) ;
}
2006-06-20 22:30:19 +04:00
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 .
*/
2008-01-08 02:04:17 +03:00
static int at91_pm_begin ( suspend_state_t state )
2006-06-20 22:30:19 +04:00
{
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 ;
2011-11-25 05:59:46 +04:00
scsr = at91_pmc_read ( AT91_PMC_SCSR ) ;
2006-06-20 22:30:19 +04:00
/* USB must not be using PLLB */
2006-12-01 13:27:31 +03:00
if ( cpu_is_at91rm9200 ( ) ) {
if ( ( scsr & ( AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP ) ) ! = 0 ) {
2009-04-01 23:33:30 +04:00
pr_err ( " AT91: PM - Suspend-to-RAM with USB still active \n " ) ;
2006-12-01 13:27:31 +03:00
return 0 ;
}
2009-06-26 18:37:01 +04:00
} else if ( cpu_is_at91sam9260 ( ) | | cpu_is_at91sam9261 ( ) | | cpu_is_at91sam9263 ( )
| | cpu_is_at91sam9g20 ( ) | | cpu_is_at91sam9g10 ( ) ) {
2007-05-31 12:34:53 +04:00
if ( ( scsr & ( AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP ) ) ! = 0 ) {
2009-04-01 23:33:30 +04:00
pr_err ( " AT91: PM - Suspend-to-RAM with USB still active \n " ) ;
2007-05-31 12:34:53 +04:00
return 0 ;
}
2006-06-20 22:30:19 +04:00
}
2012-04-30 17:00:32 +04:00
if ( ! IS_ENABLED ( CONFIG_AT91_PROGRAMMABLE_CLOCKS ) )
return 1 ;
2006-06-20 22:30:19 +04:00
/* 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 ;
2011-11-25 05:59:46 +04:00
css = at91_pmc_read ( AT91_PMC_PCKR ( i ) ) & AT91_PMC_CSS ;
2006-06-20 22:30:19 +04:00
if ( css ! = AT91_PMC_CSS_SLOW ) {
2009-04-01 23:33:30 +04:00
pr_err ( " AT91: PM - Suspend-to-RAM with PCK%d src %d \n " , i , css ) ;
2006-06-20 22:30:19 +04:00
return 0 ;
}
}
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 ) ;
2012-02-22 20:50:55 +04:00
static void ( * slow_clock ) ( void __iomem * pmc , void __iomem * ramc0 ,
void __iomem * ramc1 , int memctrl ) ;
2006-06-20 22:30:19 +04:00
2008-04-03 00:50:16 +04:00
# ifdef CONFIG_AT91_SLOW_CLOCK
2012-02-22 20:50:55 +04:00
extern void at91_slow_clock ( void __iomem * pmc , void __iomem * ramc0 ,
void __iomem * ramc1 , int memctrl ) ;
2008-04-03 00:50:16 +04:00
extern u32 at91_slow_clock_sz ;
# endif
2006-06-20 22:30:19 +04:00
static int at91_pm_enter ( suspend_state_t state )
{
2013-03-08 19:18:21 +04:00
if ( of_have_populated_dt ( ) )
at91_pinctrl_gpio_suspend ( ) ;
else
at91_gpio_suspend ( ) ;
2006-06-20 22:30:19 +04:00
at91_irq_suspend ( ) ;
pr_debug ( " AT91: PM - wake mask %08x, pm state %d \n " ,
/* remember all the always-wake irqs */
2011-11-25 05:59:46 +04:00
( at91_pmc_read ( AT91_PMC_PCSR )
2006-06-20 22:30:19 +04:00
| ( 1 < < AT91_ID_FIQ )
| ( 1 < < AT91_ID_SYS )
2013-06-01 18:40:11 +04:00
| ( at91_get_extern_irq ( ) ) )
2011-11-02 21:12:50 +04:00
& at91_aic_read ( AT91_AIC_IMR ) ,
2006-06-20 22:30:19 +04:00
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 ) {
2012-02-22 20:50:55 +04:00
int memctrl = AT91_MEMCTRL_SDRAMC ;
if ( cpu_is_at91rm9200 ( ) )
memctrl = AT91_MEMCTRL_MC ;
else if ( cpu_is_at91sam9g45 ( ) )
memctrl = AT91_MEMCTRL_DDRSDR ;
2008-04-03 00:50:16 +04:00
# ifdef CONFIG_AT91_SLOW_CLOCK
/* copy slow_clock handler to SRAM, and call it */
memcpy ( slow_clock , at91_slow_clock , at91_slow_clock_sz ) ;
# endif
2012-02-22 20:50:55 +04:00
slow_clock ( at91_pmc_base , at91_ramc_base [ 0 ] ,
at91_ramc_base [ 1 ] , memctrl ) ;
2006-06-20 22:30:19 +04:00
break ;
} else {
2008-04-03 00:50:16 +04:00
pr_info ( " AT91: PM - no slow clock mode enabled ... \n " ) ;
2006-06-20 22:30:19 +04:00
/* 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
2008-04-03 00:50:16 +04:00
* in icache so no SDRAM accesses are needed until the
* wakeup IRQ occurs and self - refresh is terminated .
2010-10-22 19:53:39 +04:00
* For ARM 926 based chips , this requirement is weaker
* as at91sam9 can access a RAM in self - refresh mode .
2006-06-20 22:30:19 +04:00
*/
2013-09-23 00:29:57 +04:00
if ( at91_pm_standby )
at91_pm_standby ( ) ;
2008-04-03 00:50:16 +04:00
break ;
2006-06-20 22:30:19 +04:00
case PM_SUSPEND_ON :
2010-10-22 19:53:39 +04:00
cpu_do_idle ( ) ;
2006-06-20 22:30:19 +04:00
break ;
default :
pr_debug ( " AT91: PM - bogus suspend state %d \n " , state ) ;
goto error ;
}
pr_debug ( " AT91: PM - wakeup %08x \n " ,
2011-11-02 21:12:50 +04:00
at91_aic_read ( AT91_AIC_IPR ) & at91_aic_read ( AT91_AIC_IMR ) ) ;
2006-06-20 22:30:19 +04:00
error :
target_state = PM_SUSPEND_ON ;
at91_irq_resume ( ) ;
2013-03-08 19:18:21 +04:00
if ( of_have_populated_dt ( ) )
at91_pinctrl_gpio_resume ( ) ;
else
at91_gpio_resume ( ) ;
2006-06-20 22:30:19 +04:00
return 0 ;
}
2008-01-08 02:04:17 +03:00
/*
* Called right prior to thawing processes .
*/
static void at91_pm_end ( void )
{
target_state = PM_SUSPEND_ON ;
}
2006-06-20 22:30:19 +04:00
2010-11-16 16:14:02 +03:00
static const struct platform_suspend_ops at91_pm_ops = {
2008-01-08 02:04:17 +03:00
. valid = at91_pm_valid_state ,
. begin = at91_pm_begin ,
. enter = at91_pm_enter ,
. end = at91_pm_end ,
2006-06-20 22:30:19 +04:00
} ;
2013-09-23 00:29:57 +04:00
static struct platform_device at91_cpuidle_device = {
. name = " cpuidle-at91 " ,
} ;
void at91_pm_set_standby ( void ( * at91_standby ) ( void ) )
{
if ( at91_standby ) {
at91_cpuidle_device . dev . platform_data = at91_standby ;
at91_pm_standby = at91_standby ;
}
}
2006-06-20 22:30:19 +04:00
static int __init at91_pm_init ( void )
{
2008-04-03 00:50:16 +04:00
# ifdef CONFIG_AT91_SLOW_CLOCK
slow_clock = ( void * ) ( AT91_IO_VIRT_BASE - at91_slow_clock_sz ) ;
2006-06-20 22:30:19 +04:00
# endif
2008-04-03 00:50:16 +04:00
pr_info ( " AT91: Power Management%s \n " , ( slow_clock ? " (with slow clock mode) " : " " ) ) ;
/* AT91RM9200 SDRAM low-power mode cannot be used with self-refresh. */
2012-02-13 10:58:30 +04:00
if ( cpu_is_at91rm9200 ( ) )
at91_ramc_write ( 0 , AT91RM9200_SDRAMC_LPR , 0 ) ;
2013-09-23 00:29:57 +04:00
if ( at91_cpuidle_device . dev . platform_data )
platform_device_register ( & at91_cpuidle_device ) ;
2006-06-20 22:30:19 +04:00
2007-10-18 14:04:40 +04:00
suspend_set_ops ( & at91_pm_ops ) ;
2006-06-20 22:30:19 +04:00
2008-04-03 00:52:19 +04:00
show_reset_status ( ) ;
2006-06-20 22:30:19 +04:00
return 0 ;
}
arch_initcall ( at91_pm_init ) ;