2011-04-29 02:28:08 +09:00
/*
* sh7372 Power management support
*
* Copyright ( C ) 2011 Magnus Damm
*
* This file is subject to the terms and conditions of the GNU General Public
* License . See the file " COPYING " in the main directory of this archive
* for more details .
*/
# include <linux/pm.h>
# include <linux/suspend.h>
2011-04-29 02:39:32 +09:00
# include <linux/cpuidle.h>
2011-04-29 02:28:08 +09:00
# include <linux/module.h>
# include <linux/list.h>
# include <linux/err.h>
# include <linux/slab.h>
2011-08-25 15:34:19 +02:00
# include <linux/pm_clock.h>
2011-07-01 22:13:56 +02:00
# include <linux/platform_device.h>
# include <linux/delay.h>
2011-04-29 02:28:08 +09:00
# include <asm/system.h>
# include <asm/io.h>
# include <asm/tlbflush.h>
# include <mach/common.h>
2011-07-01 22:13:56 +02:00
# include <mach/sh7372.h>
2011-04-29 02:28:08 +09:00
# define SMFRAM 0xe6a70000
# define SYSTBCR 0xe6150024
# define SBAR 0xe6180020
# define APARMBAREA 0xe6f10020
2011-07-01 22:13:56 +02:00
# define SPDCR 0xe6180008
# define SWUCR 0xe6180014
# define PSTR 0xe6180080
# define PSTR_RETRIES 100
# define PSTR_DELAY_US 10
# ifdef CONFIG_PM
static int pd_power_down ( struct generic_pm_domain * genpd )
{
struct sh7372_pm_domain * sh7372_pd = to_sh7372_pd ( genpd ) ;
unsigned int mask = 1 < < sh7372_pd - > bit_shift ;
if ( __raw_readl ( PSTR ) & mask ) {
unsigned int retry_count ;
__raw_writel ( mask , SPDCR ) ;
for ( retry_count = PSTR_RETRIES ; retry_count ; retry_count - - ) {
if ( ! ( __raw_readl ( SPDCR ) & mask ) )
break ;
cpu_relax ( ) ;
}
}
pr_debug ( " sh7372 power domain down 0x%08x -> PSTR = 0x%08x \n " ,
mask , __raw_readl ( PSTR ) ) ;
return 0 ;
}
static int pd_power_up ( struct generic_pm_domain * genpd )
{
struct sh7372_pm_domain * sh7372_pd = to_sh7372_pd ( genpd ) ;
unsigned int mask = 1 < < sh7372_pd - > bit_shift ;
unsigned int retry_count ;
int ret = 0 ;
if ( __raw_readl ( PSTR ) & mask )
goto out ;
__raw_writel ( mask , SWUCR ) ;
for ( retry_count = 2 * PSTR_RETRIES ; retry_count ; retry_count - - ) {
if ( ! ( __raw_readl ( SWUCR ) & mask ) )
goto out ;
if ( retry_count > PSTR_RETRIES )
udelay ( PSTR_DELAY_US ) ;
else
cpu_relax ( ) ;
}
if ( __raw_readl ( SWUCR ) & mask )
ret = - EIO ;
out :
pr_debug ( " sh7372 power domain up 0x%08x -> PSTR = 0x%08x \n " ,
mask , __raw_readl ( PSTR ) ) ;
return ret ;
}
static bool pd_active_wakeup ( struct device * dev )
{
return true ;
}
void sh7372_init_pm_domain ( struct sh7372_pm_domain * sh7372_pd )
{
struct generic_pm_domain * genpd = & sh7372_pd - > genpd ;
pm_genpd_init ( genpd , NULL , false ) ;
genpd - > stop_device = pm_clk_suspend ;
genpd - > start_device = pm_clk_resume ;
genpd - > active_wakeup = pd_active_wakeup ;
2011-08-14 13:35:39 +02:00
genpd - > power_off = pd_power_down ;
genpd - > power_on = pd_power_up ;
2011-07-10 10:39:32 +02:00
genpd - > power_on ( & sh7372_pd - > genpd ) ;
2011-07-01 22:13:56 +02:00
}
void sh7372_add_device_to_domain ( struct sh7372_pm_domain * sh7372_pd ,
struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
pm_genpd_add_device ( & sh7372_pd - > genpd , dev ) ;
2011-08-25 15:34:12 +02:00
if ( pm_clk_no_clocks ( dev ) )
pm_clk_add ( dev , NULL ) ;
2011-07-01 22:13:56 +02:00
}
2011-08-14 13:35:39 +02:00
void sh7372_pm_add_subdomain ( struct sh7372_pm_domain * sh7372_pd ,
struct sh7372_pm_domain * sh7372_sd )
{
pm_genpd_add_subdomain ( & sh7372_pd - > genpd , & sh7372_sd - > genpd ) ;
}
2011-07-01 22:13:56 +02:00
struct sh7372_pm_domain sh7372_a4lc = {
. bit_shift = 1 ,
} ;
2011-07-10 10:12:08 +02:00
struct sh7372_pm_domain sh7372_a4mp = {
. bit_shift = 2 ,
} ;
2011-07-10 10:38:22 +02:00
struct sh7372_pm_domain sh7372_d4 = {
. bit_shift = 3 ,
} ;
2011-07-01 22:14:45 +02:00
struct sh7372_pm_domain sh7372_a3rv = {
. bit_shift = 6 ,
} ;
2011-07-01 22:14:53 +02:00
struct sh7372_pm_domain sh7372_a3ri = {
. bit_shift = 8 ,
} ;
2011-07-01 22:15:01 +02:00
struct sh7372_pm_domain sh7372_a3sg = {
. bit_shift = 13 ,
} ;
2011-07-01 22:13:56 +02:00
# endif /* CONFIG_PM */
2011-05-25 11:22:58 +09:00
static void sh7372_enter_core_standby ( void )
2011-04-29 02:28:08 +09:00
{
void __iomem * smfram = ( void __iomem * ) SMFRAM ;
__raw_writel ( 0 , APARMBAREA ) ; /* translate 4k */
__raw_writel ( __pa ( sh7372_cpu_resume ) , SBAR ) ; /* set reset vector */
__raw_writel ( 0x10 , SYSTBCR ) ; /* enable core standby */
__raw_writel ( 0 , smfram + 0x3c ) ; /* clear page table address */
sh7372_cpu_suspend ( ) ;
cpu_init ( ) ;
/* if page table address is non-NULL then we have been powered down */
if ( __raw_readl ( smfram + 0x3c ) ) {
__raw_writel ( __raw_readl ( smfram + 0x40 ) ,
__va ( __raw_readl ( smfram + 0x3c ) ) ) ;
flush_tlb_all ( ) ;
set_cr ( __raw_readl ( smfram + 0x38 ) ) ;
}
__raw_writel ( 0 , SYSTBCR ) ; /* disable core standby */
__raw_writel ( 0 , SBAR ) ; /* disable reset vector translation */
}
2011-04-29 02:39:32 +09:00
# ifdef CONFIG_CPU_IDLE
static void sh7372_cpuidle_setup ( struct cpuidle_device * dev )
{
struct cpuidle_state * state ;
int i = dev - > state_count ;
state = & dev - > states [ i ] ;
snprintf ( state - > name , CPUIDLE_NAME_LEN , " C2 " ) ;
strncpy ( state - > desc , " Core Standby Mode " , CPUIDLE_DESC_LEN ) ;
state - > exit_latency = 10 ;
state - > target_residency = 20 + 10 ;
state - > power_usage = 1 ; /* perhaps not */
state - > flags = 0 ;
state - > flags | = CPUIDLE_FLAG_TIME_VALID ;
shmobile_cpuidle_modes [ i ] = sh7372_enter_core_standby ;
dev - > state_count = i + 1 ;
}
static void sh7372_cpuidle_init ( void )
{
shmobile_cpuidle_setup = sh7372_cpuidle_setup ;
}
# else
static void sh7372_cpuidle_init ( void ) { }
# endif
# ifdef CONFIG_SUSPEND
2011-04-29 02:28:08 +09:00
static int sh7372_enter_suspend ( suspend_state_t suspend_state )
{
sh7372_enter_core_standby ( ) ;
return 0 ;
}
static void sh7372_suspend_init ( void )
{
shmobile_suspend_ops . enter = sh7372_enter_suspend ;
}
# else
static void sh7372_suspend_init ( void ) { }
# endif
# define DBGREG1 0xe6100020
# define DBGREG9 0xe6100040
void __init sh7372_pm_init ( void )
{
/* enable DBG hardware block to kick SYSC */
__raw_writel ( 0x0000a500 , DBGREG9 ) ;
__raw_writel ( 0x0000a501 , DBGREG9 ) ;
__raw_writel ( 0x00000000 , DBGREG1 ) ;
sh7372_suspend_init ( ) ;
2011-04-29 02:39:32 +09:00
sh7372_cpuidle_init ( ) ;
2011-04-29 02:28:08 +09:00
}