2006-06-20 19:30:19 +01:00
/*
2007-02-05 11:42:07 +01:00
* arch / arm / mach - at91 / pm . c
2006-06-20 19:30:19 +01: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 10:53:52 +01:00
# include <linux/gpio.h>
2007-10-18 03:04:39 -07:00
# include <linux/suspend.h>
2006-06-20 19:30:19 +01:00
# include <linux/sched.h>
# include <linux/proc_fs.h>
2015-01-15 15:59:25 +01:00
# include <linux/genalloc.h>
2006-06-20 19:30:19 +01:00
# include <linux/interrupt.h>
# include <linux/sysfs.h>
# include <linux/module.h>
2015-01-15 15:59:24 +01:00
# include <linux/of.h>
2015-01-15 15:59:25 +01:00
# include <linux/of_platform.h>
2015-01-27 17:38:46 +01:00
# include <linux/of_address.h>
2006-06-20 19:30:19 +01:00
# include <linux/platform_device.h>
2008-09-06 12:10:45 +01:00
# include <linux/io.h>
2013-10-11 09:37:45 +02:00
# include <linux/clk/at91_pmc.h>
2006-06-20 19:30:19 +01:00
# include <asm/irq.h>
2011-07-26 16:09:06 -07:00
# include <linux/atomic.h>
2006-06-20 19:30:19 +01:00
# include <asm/mach/time.h>
# include <asm/mach/irq.h>
2015-03-09 11:49:01 +08:00
# include <asm/fncpy.h>
2015-03-09 11:54:26 +08:00
# include <asm/cacheflush.h>
2015-09-30 01:58:40 +02:00
# include <asm/system_misc.h>
2006-06-20 19:30:19 +01:00
# include "generic.h"
2009-11-01 18:40:50 +01:00
# include "pm.h"
2006-06-20 19:30:19 +01:00
2015-09-30 01:31:34 +02:00
static void __iomem * pmc ;
2015-03-13 22:57:23 +01:00
/*
* FIXME : this is needed to communicate between the pinctrl driver and
* the PM implementation in the machine . Possibly part of the PM
* implementation should be moved down into the pinctrl driver and get
* called as part of the generic suspend / resume path .
*/
2015-12-01 11:44:40 +01:00
# ifdef CONFIG_PINCTRL_AT91
2015-03-13 22:57:23 +01:00
extern void at91_pinctrl_gpio_suspend ( void ) ;
extern void at91_pinctrl_gpio_resume ( void ) ;
2015-12-01 11:44:40 +01:00
# endif
2015-03-13 22:57:23 +01:00
2015-01-15 15:59:24 +01:00
static struct {
unsigned long uhp_udp_mask ;
int memctrl ;
} at91_pm_data ;
2015-01-27 17:38:46 +01:00
void __iomem * at91_ramc_base [ 2 ] ;
2013-09-22 22:29:57 +02:00
2006-06-20 19:30:19 +01: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 00:04:17 +01:00
static int at91_pm_begin ( suspend_state_t state )
2006-06-20 19:30:19 +01: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 ;
2015-09-30 01:31:34 +02:00
scsr = readl ( pmc + AT91_PMC_SCSR ) ;
2006-06-20 19:30:19 +01:00
/* USB must not be using PLLB */
2015-01-15 15:59:24 +01:00
if ( ( scsr & at91_pm_data . uhp_udp_mask ) ! = 0 ) {
pr_err ( " AT91: PM - Suspend-to-RAM with USB still active \n " ) ;
return 0 ;
2006-06-20 19:30:19 +01: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 ;
2015-09-30 01:31:34 +02:00
css = readl ( pmc + AT91_PMC_PCKR ( i ) ) & AT91_PMC_CSS ;
2006-06-20 19:30:19 +01:00
if ( css ! = AT91_PMC_CSS_SLOW ) {
2009-04-01 20:33:30 +01:00
pr_err ( " AT91: PM - Suspend-to-RAM with PCK%d src %d \n " , i , css ) ;
2006-06-20 19:30:19 +01: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 ) ;
2015-03-09 11:51:09 +08:00
static void ( * at91_suspend_sram_fn ) ( void __iomem * pmc , void __iomem * ramc0 ,
2012-02-22 17:50:55 +01:00
void __iomem * ramc1 , int memctrl ) ;
2006-06-20 19:30:19 +01:00
2015-03-09 11:51:09 +08:00
extern void at91_pm_suspend_in_sram ( void __iomem * pmc , void __iomem * ramc0 ,
2012-02-22 17:50:55 +01:00
void __iomem * ramc1 , int memctrl ) ;
2015-03-09 11:51:09 +08:00
extern u32 at91_pm_suspend_in_sram_sz ;
2008-04-02 21:50:16 +01:00
2015-03-09 11:49:46 +08:00
static void at91_pm_suspend ( suspend_state_t state )
{
unsigned int pm_data = at91_pm_data . memctrl ;
pm_data | = ( state = = PM_SUSPEND_MEM ) ?
AT91_PM_MODE ( AT91_PM_SLOW_CLOCK ) : 0 ;
2015-03-09 11:54:26 +08:00
flush_cache_all ( ) ;
outer_disable ( ) ;
2015-09-30 01:31:34 +02:00
at91_suspend_sram_fn ( pmc , at91_ramc_base [ 0 ] ,
at91_ramc_base [ 1 ] , pm_data ) ;
2015-03-09 11:54:26 +08:00
outer_resume ( ) ;
2015-03-09 11:49:46 +08:00
}
2006-06-20 19:30:19 +01:00
static int at91_pm_enter ( suspend_state_t state )
{
2015-12-01 11:44:40 +01:00
# ifdef CONFIG_PINCTRL_AT91
2014-12-02 12:08:27 +01:00
at91_pinctrl_gpio_suspend ( ) ;
2015-12-01 11:44:40 +01:00
# endif
2006-06-20 19:30:19 +01:00
switch ( state ) {
2015-03-09 11:49:46 +08:00
/*
* Suspend - to - RAM is like STANDBY plus slow clock mode , so
* drivers must suspend more deeply , the master clock switches
* to the clk32k and turns off the main oscillator
*/
case PM_SUSPEND_MEM :
2006-06-20 19:30:19 +01:00
/*
2015-03-09 11:49:46 +08:00
* Ensure that clocks are in a valid state .
2006-06-20 19:30:19 +01:00
*/
2015-03-09 11:49:46 +08:00
if ( ! at91_pm_verify_clocks ( ) )
goto error ;
2006-06-20 19:30:19 +01:00
2015-03-09 11:49:46 +08:00
at91_pm_suspend ( state ) ;
2006-06-20 19:30:19 +01:00
2015-03-09 11:49:46 +08:00
break ;
2006-06-20 19:30:19 +01:00
2015-03-09 11:49:46 +08:00
/*
* 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 :
at91_pm_suspend ( state ) ;
break ;
case PM_SUSPEND_ON :
cpu_do_idle ( ) ;
break ;
default :
pr_debug ( " AT91: PM - bogus suspend state %d \n " , state ) ;
goto error ;
2006-06-20 19:30:19 +01:00
}
error :
target_state = PM_SUSPEND_ON ;
2014-07-10 19:14:20 +02:00
2015-12-01 11:44:40 +01:00
# ifdef CONFIG_PINCTRL_AT91
2014-12-02 12:08:27 +01:00
at91_pinctrl_gpio_resume ( ) ;
2015-12-01 11:44:40 +01:00
# endif
2006-06-20 19:30:19 +01:00
return 0 ;
}
2008-01-08 00:04:17 +01:00
/*
* Called right prior to thawing processes .
*/
static void at91_pm_end ( void )
{
target_state = PM_SUSPEND_ON ;
}
2006-06-20 19:30:19 +01:00
2010-11-16 14:14:02 +01:00
static const struct platform_suspend_ops at91_pm_ops = {
2008-01-08 00:04:17 +01:00
. valid = at91_pm_valid_state ,
. begin = at91_pm_begin ,
. enter = at91_pm_enter ,
. end = at91_pm_end ,
2006-06-20 19:30:19 +01:00
} ;
2013-09-22 22:29:57 +02:00
static struct platform_device at91_cpuidle_device = {
. name = " cpuidle-at91 " ,
} ;
2015-03-04 09:44:45 +08:00
static void at91_pm_set_standby ( void ( * at91_standby ) ( void ) )
2013-09-22 22:29:57 +02:00
{
2015-03-09 11:51:49 +08:00
if ( at91_standby )
2013-09-22 22:29:57 +02:00
at91_cpuidle_device . dev . platform_data = at91_standby ;
}
2015-03-16 23:44:37 +01:00
/*
* 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 void at91rm9200_standby ( void )
{
2015-03-16 15:14:50 +01:00
u32 lpr = at91_ramc_read ( 0 , AT91_MC_SDRAMC_LPR ) ;
2015-03-16 23:44:37 +01:00
asm volatile (
" b 1f \n \t "
" .align 5 \n \t "
" 1: mcr p15, 0, %0, c7, c10, 4 \n \t "
" str %0, [%1, %2] \n \t "
" str %3, [%1, %4] \n \t "
" mcr p15, 0, %0, c7, c0, 4 \n \t "
" str %5, [%1, %2] "
:
2015-03-16 15:14:50 +01:00
: " r " ( 0 ) , " r " ( at91_ramc_base [ 0 ] ) , " r " ( AT91_MC_SDRAMC_LPR ) ,
" r " ( 1 ) , " r " ( AT91_MC_SDRAMC_SRR ) ,
2015-03-16 23:44:37 +01:00
" r " ( lpr ) ) ;
}
/* We manage both DDRAM/SDRAM controllers, we need more than one value to
* remember .
*/
static void at91_ddr_standby ( void )
{
/* Those two values allow us to delay self-refresh activation
* to the maximum . */
u32 lpr0 , lpr1 = 0 ;
u32 saved_lpr0 , saved_lpr1 = 0 ;
if ( at91_ramc_base [ 1 ] ) {
saved_lpr1 = at91_ramc_read ( 1 , AT91_DDRSDRC_LPR ) ;
lpr1 = saved_lpr1 & ~ AT91_DDRSDRC_LPCB ;
lpr1 | = AT91_DDRSDRC_LPCB_SELF_REFRESH ;
}
saved_lpr0 = at91_ramc_read ( 0 , AT91_DDRSDRC_LPR ) ;
lpr0 = saved_lpr0 & ~ AT91_DDRSDRC_LPCB ;
lpr0 | = AT91_DDRSDRC_LPCB_SELF_REFRESH ;
/* self-refresh mode now */
at91_ramc_write ( 0 , AT91_DDRSDRC_LPR , lpr0 ) ;
if ( at91_ramc_base [ 1 ] )
at91_ramc_write ( 1 , AT91_DDRSDRC_LPR , lpr1 ) ;
cpu_do_idle ( ) ;
at91_ramc_write ( 0 , AT91_DDRSDRC_LPR , saved_lpr0 ) ;
if ( at91_ramc_base [ 1 ] )
at91_ramc_write ( 1 , AT91_DDRSDRC_LPR , saved_lpr1 ) ;
}
/* We manage both DDRAM/SDRAM controllers, we need more than one value to
* remember .
*/
static void at91sam9_sdram_standby ( void )
{
u32 lpr0 , lpr1 = 0 ;
u32 saved_lpr0 , saved_lpr1 = 0 ;
if ( at91_ramc_base [ 1 ] ) {
saved_lpr1 = at91_ramc_read ( 1 , AT91_SDRAMC_LPR ) ;
lpr1 = saved_lpr1 & ~ AT91_SDRAMC_LPCB ;
lpr1 | = AT91_SDRAMC_LPCB_SELF_REFRESH ;
}
saved_lpr0 = at91_ramc_read ( 0 , AT91_SDRAMC_LPR ) ;
lpr0 = saved_lpr0 & ~ AT91_SDRAMC_LPCB ;
lpr0 | = AT91_SDRAMC_LPCB_SELF_REFRESH ;
/* self-refresh mode now */
at91_ramc_write ( 0 , AT91_SDRAMC_LPR , lpr0 ) ;
if ( at91_ramc_base [ 1 ] )
at91_ramc_write ( 1 , AT91_SDRAMC_LPR , lpr1 ) ;
cpu_do_idle ( ) ;
at91_ramc_write ( 0 , AT91_SDRAMC_LPR , saved_lpr0 ) ;
if ( at91_ramc_base [ 1 ] )
at91_ramc_write ( 1 , AT91_SDRAMC_LPR , saved_lpr1 ) ;
}
2015-07-27 18:27:52 -04:00
static const struct of_device_id const ramc_ids [ ] __initconst = {
2015-01-27 17:38:46 +01:00
{ . compatible = " atmel,at91rm9200-sdramc " , . data = at91rm9200_standby } ,
{ . compatible = " atmel,at91sam9260-sdramc " , . data = at91sam9_sdram_standby } ,
{ . compatible = " atmel,at91sam9g45-ddramc " , . data = at91_ddr_standby } ,
{ . compatible = " atmel,sama5d3-ddramc " , . data = at91_ddr_standby } ,
{ /*sentinel*/ }
} ;
2015-02-18 21:19:56 +01:00
static __init void at91_dt_ramc ( void )
2015-01-27 17:38:46 +01:00
{
struct device_node * np ;
const struct of_device_id * of_id ;
int idx = 0 ;
const void * standby = NULL ;
for_each_matching_node_and_match ( np , ramc_ids , & of_id ) {
at91_ramc_base [ idx ] = of_iomap ( np , 0 ) ;
if ( ! at91_ramc_base [ idx ] )
panic ( pr_fmt ( " unable to map ramc[%d] cpu registers \n " ) , idx ) ;
if ( ! standby )
standby = of_id - > data ;
idx + + ;
}
if ( ! idx )
panic ( pr_fmt ( " unable to find compatible ram controller node in dtb \n " ) ) ;
if ( ! standby ) {
pr_warn ( " ramc no standby function available \n " ) ;
return ;
}
at91_pm_set_standby ( standby ) ;
}
2015-09-30 01:58:40 +02:00
void at91rm9200_idle ( void )
{
/*
* Disable the processor clock . The processor will be automatically
* re - enabled by an interrupt or by a reset .
*/
writel ( AT91_PMC_PCK , pmc + AT91_PMC_SCDR ) ;
}
void at91sam9_idle ( void )
{
writel ( AT91_PMC_PCK , pmc + AT91_PMC_SCDR ) ;
cpu_do_idle ( ) ;
}
2015-01-15 15:59:25 +01:00
static void __init at91_pm_sram_init ( void )
{
struct gen_pool * sram_pool ;
phys_addr_t sram_pbase ;
unsigned long sram_base ;
struct device_node * node ;
2015-03-03 08:38:07 +01:00
struct platform_device * pdev = NULL ;
2015-01-15 15:59:25 +01:00
2015-03-03 08:38:07 +01:00
for_each_compatible_node ( node , NULL , " mmio-sram " ) {
pdev = of_find_device_by_node ( node ) ;
if ( pdev ) {
of_node_put ( node ) ;
break ;
}
2015-01-15 15:59:25 +01:00
}
if ( ! pdev ) {
pr_warn ( " %s: failed to find sram device! \n " , __func__ ) ;
2015-03-03 08:38:07 +01:00
return ;
2015-01-15 15:59:25 +01:00
}
2015-09-04 15:47:43 -07:00
sram_pool = gen_pool_get ( & pdev - > dev , NULL ) ;
2015-01-15 15:59:25 +01:00
if ( ! sram_pool ) {
pr_warn ( " %s: sram pool unavailable! \n " , __func__ ) ;
2015-03-03 08:38:07 +01:00
return ;
2015-01-15 15:59:25 +01:00
}
2015-03-09 11:51:09 +08:00
sram_base = gen_pool_alloc ( sram_pool , at91_pm_suspend_in_sram_sz ) ;
2015-01-15 15:59:25 +01:00
if ( ! sram_base ) {
2015-03-09 11:51:09 +08:00
pr_warn ( " %s: unable to alloc sram! \n " , __func__ ) ;
2015-03-03 08:38:07 +01:00
return ;
2015-01-15 15:59:25 +01:00
}
sram_pbase = gen_pool_virt_to_phys ( sram_pool , sram_base ) ;
2015-03-09 11:51:09 +08:00
at91_suspend_sram_fn = __arm_ioremap_exec ( sram_pbase ,
at91_pm_suspend_in_sram_sz , false ) ;
if ( ! at91_suspend_sram_fn ) {
2015-03-09 11:49:01 +08:00
pr_warn ( " SRAM: Could not map \n " ) ;
return ;
}
2015-03-09 11:51:09 +08:00
/* Copy the pm suspend handler to SRAM */
at91_suspend_sram_fn = fncpy ( at91_suspend_sram_fn ,
& at91_pm_suspend_in_sram , at91_pm_suspend_in_sram_sz ) ;
2015-01-15 15:59:25 +01:00
}
2015-09-30 01:31:34 +02:00
static const struct of_device_id atmel_pmc_ids [ ] __initconst = {
{ . compatible = " atmel,at91rm9200-pmc " } ,
{ . compatible = " atmel,at91sam9260-pmc " } ,
{ . compatible = " atmel,at91sam9g45-pmc " } ,
{ . compatible = " atmel,at91sam9n12-pmc " } ,
{ . compatible = " atmel,at91sam9x5-pmc " } ,
{ . compatible = " atmel,sama5d3-pmc " } ,
{ . compatible = " atmel,sama5d2-pmc " } ,
{ /* sentinel */ } ,
} ;
2015-09-30 01:58:40 +02:00
static void __init at91_pm_init ( void ( * pm_idle ) ( void ) )
2006-06-20 19:30:19 +01:00
{
2015-09-30 01:31:34 +02:00
struct device_node * pmc_np ;
2008-04-02 21:50:16 +01:00
2013-09-22 22:29:57 +02:00
if ( at91_cpuidle_device . dev . platform_data )
platform_device_register ( & at91_cpuidle_device ) ;
2006-06-20 19:30:19 +01:00
2015-09-30 01:31:34 +02:00
pmc_np = of_find_matching_node ( NULL , atmel_pmc_ids ) ;
pmc = of_iomap ( pmc_np , 0 ) ;
if ( ! pmc ) {
pr_err ( " AT91: PM not supported, PMC not found \n " ) ;
return ;
}
2015-09-30 01:58:40 +02:00
if ( pm_idle )
arm_pm_idle = pm_idle ;
2015-09-30 01:31:34 +02:00
at91_pm_sram_init ( ) ;
2015-03-09 11:51:09 +08:00
if ( at91_suspend_sram_fn )
2015-03-09 11:49:01 +08:00
suspend_set_ops ( & at91_pm_ops ) ;
else
pr_info ( " AT91: PM not supported, due to no SRAM allocated \n " ) ;
2015-01-15 15:59:27 +01:00
}
2006-06-20 19:30:19 +01:00
2015-01-27 18:41:33 +01:00
void __init at91rm9200_pm_init ( void )
2015-01-15 15:59:27 +01:00
{
2015-01-27 17:38:46 +01:00
at91_dt_ramc ( ) ;
2015-01-15 15:59:27 +01:00
/*
* AT91RM9200 SDRAM low - power mode cannot be used with self - refresh .
*/
2015-03-16 15:14:50 +01:00
at91_ramc_write ( 0 , AT91_MC_SDRAMC_LPR , 0 ) ;
2015-01-15 15:59:27 +01:00
at91_pm_data . uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP ;
at91_pm_data . memctrl = AT91_MEMCTRL_MC ;
2015-09-30 01:58:40 +02:00
at91_pm_init ( at91rm9200_idle ) ;
2015-01-15 15:59:27 +01:00
}
2015-01-27 18:41:33 +01:00
void __init at91sam9260_pm_init ( void )
2015-01-15 15:59:27 +01:00
{
2015-01-27 17:38:46 +01:00
at91_dt_ramc ( ) ;
2015-01-15 15:59:27 +01:00
at91_pm_data . memctrl = AT91_MEMCTRL_SDRAMC ;
at91_pm_data . uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP ;
2015-09-30 01:58:40 +02:00
at91_pm_init ( at91sam9_idle ) ;
2015-01-15 15:59:27 +01:00
}
2015-01-27 18:41:33 +01:00
void __init at91sam9g45_pm_init ( void )
2015-01-15 15:59:27 +01:00
{
2015-01-27 17:38:46 +01:00
at91_dt_ramc ( ) ;
2015-01-15 15:59:27 +01:00
at91_pm_data . uhp_udp_mask = AT91SAM926x_PMC_UHP ;
at91_pm_data . memctrl = AT91_MEMCTRL_DDRSDR ;
2015-09-30 01:58:40 +02:00
at91_pm_init ( at91sam9_idle ) ;
2006-06-20 19:30:19 +01:00
}
2015-01-22 16:54:50 +01:00
2015-01-27 18:41:33 +01:00
void __init at91sam9x5_pm_init ( void )
2015-01-22 16:54:50 +01:00
{
2015-01-27 17:38:46 +01:00
at91_dt_ramc ( ) ;
2015-01-22 16:54:50 +01:00
at91_pm_data . uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP ;
at91_pm_data . memctrl = AT91_MEMCTRL_DDRSDR ;
2015-09-30 01:58:40 +02:00
at91_pm_init ( at91sam9_idle ) ;
}
void __init sama5_pm_init ( void )
{
at91_dt_ramc ( ) ;
at91_pm_data . uhp_udp_mask = AT91SAM926x_PMC_UHP | AT91SAM926x_PMC_UDP ;
at91_pm_data . memctrl = AT91_MEMCTRL_DDRSDR ;
at91_pm_init ( NULL ) ;
2015-01-22 16:54:50 +01:00
}