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>
2006-06-20 19:30:19 +01:00
2008-08-05 16:14:15 +01:00
# include <mach/cpu.h>
2013-11-14 10:49:19 +01:00
# include <mach/hardware.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-01-15 15:59:24 +01:00
static struct {
unsigned long uhp_udp_mask ;
int memctrl ;
} at91_pm_data ;
2013-09-22 22:29:57 +02:00
static void ( * at91_pm_standby ) ( void ) ;
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 ;
2011-11-25 09:59:46 +08:00
scsr = at91_pmc_read ( 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 ;
2011-11-25 09:59:46 +08:00
css = at91_pmc_read ( 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:51:09 +08:00
at91_suspend_sram_fn ( at91_pmc_base , at91_ramc_base [ 0 ] ,
at91_ramc_base [ 1 ] , pm_data ) ;
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 )
{
2014-12-02 12:08:27 +01:00
at91_pinctrl_gpio_suspend ( ) ;
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
2014-12-02 12:08:27 +01:00
at91_pinctrl_gpio_resume ( ) ;
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
{
if ( at91_standby ) {
at91_cpuidle_device . dev . platform_data = at91_standby ;
at91_pm_standby = at91_standby ;
}
}
2015-02-18 21:19:56 +01:00
static const struct of_device_id 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-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
}
sram_pool = dev_get_gen_pool ( & pdev - > dev ) ;
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-01-15 15:59:27 +01:00
static void __init at91_pm_init ( void )
2006-06-20 19:30:19 +01:00
{
2015-01-15 15:59:25 +01:00
at91_pm_sram_init ( ) ;
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-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 .
*/
at91_ramc_write ( 0 , AT91RM9200_SDRAMC_LPR , 0 ) ;
at91_pm_data . uhp_udp_mask = AT91RM9200_PMC_UHP | AT91RM9200_PMC_UDP ;
at91_pm_data . memctrl = AT91_MEMCTRL_MC ;
at91_pm_init ( ) ;
}
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 ;
return at91_pm_init ( ) ;
}
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 ;
return at91_pm_init ( ) ;
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 ;
return at91_pm_init ( ) ;
}