2012-01-10 10:50:01 +04:00
/*
* r8a7779 Power management support
*
* Copyright ( C ) 2011 Renesas Solutions Corp .
* 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>
# include <linux/err.h>
# include <linux/pm_clock.h>
# include <linux/platform_device.h>
# include <linux/delay.h>
# include <linux/irq.h>
# include <linux/interrupt.h>
# include <linux/console.h>
# include <asm/io.h>
# include <mach/common.h>
# include <mach/r8a7779.h>
static void __iomem * r8a7779_sysc_base ;
/* SYSC */
# define SYSCSR 0x00
# define SYSCISR 0x04
# define SYSCISCR 0x08
# define SYSCIER 0x0c
# define SYSCIMR 0x10
# define PWRSR0 0x40
# define PWRSR1 0x80
# define PWRSR2 0xc0
# define PWRSR3 0x100
# define PWRSR4 0x140
# define PWRSR_OFFS 0x00
# define PWROFFCR_OFFS 0x04
# define PWRONCR_OFFS 0x0c
# define PWRER_OFFS 0x14
# define SYSCSR_RETRIES 100
# define SYSCSR_DELAY_US 1
# define SYSCISR_RETRIES 1000
# define SYSCISR_DELAY_US 1
2012-01-10 12:44:39 +04:00
# if defined(CONFIG_PM) || defined(CONFIG_SMP)
static DEFINE_SPINLOCK ( r8a7779_sysc_lock ) ; /* SMP CPUs + I/O devices */
2012-01-10 10:50:01 +04:00
static int r8a7779_sysc_pwr_on_off ( struct r8a7779_pm_ch * r8a7779_ch ,
int sr_bit , int reg_offs )
{
int k ;
for ( k = 0 ; k < SYSCSR_RETRIES ; k + + ) {
if ( ioread32 ( r8a7779_sysc_base + SYSCSR ) & ( 1 < < sr_bit ) )
break ;
udelay ( SYSCSR_DELAY_US ) ;
}
if ( k = = SYSCSR_RETRIES )
return - EAGAIN ;
iowrite32 ( 1 < < r8a7779_ch - > chan_bit ,
r8a7779_sysc_base + r8a7779_ch - > chan_offs + reg_offs ) ;
return 0 ;
}
static int r8a7779_sysc_pwr_off ( struct r8a7779_pm_ch * r8a7779_ch )
{
return r8a7779_sysc_pwr_on_off ( r8a7779_ch , 0 , PWROFFCR_OFFS ) ;
}
static int r8a7779_sysc_pwr_on ( struct r8a7779_pm_ch * r8a7779_ch )
{
return r8a7779_sysc_pwr_on_off ( r8a7779_ch , 1 , PWRONCR_OFFS ) ;
}
static int r8a7779_sysc_update ( struct r8a7779_pm_ch * r8a7779_ch ,
int ( * on_off_fn ) ( struct r8a7779_pm_ch * ) )
{
unsigned int isr_mask = 1 < < r8a7779_ch - > isr_bit ;
unsigned int chan_mask = 1 < < r8a7779_ch - > chan_bit ;
unsigned int status ;
2012-01-10 12:44:39 +04:00
unsigned long flags ;
2012-01-10 10:50:01 +04:00
int ret = 0 ;
int k ;
2012-01-10 12:44:39 +04:00
spin_lock_irqsave ( & r8a7779_sysc_lock , flags ) ;
2012-01-10 10:50:01 +04:00
iowrite32 ( isr_mask , r8a7779_sysc_base + SYSCISCR ) ;
do {
ret = on_off_fn ( r8a7779_ch ) ;
if ( ret )
goto out ;
status = ioread32 ( r8a7779_sysc_base +
r8a7779_ch - > chan_offs + PWRER_OFFS ) ;
} while ( status & chan_mask ) ;
for ( k = 0 ; k < SYSCISR_RETRIES ; k + + ) {
if ( ioread32 ( r8a7779_sysc_base + SYSCISR ) & isr_mask )
break ;
udelay ( SYSCISR_DELAY_US ) ;
}
if ( k = = SYSCISR_RETRIES )
ret = - EIO ;
iowrite32 ( isr_mask , r8a7779_sysc_base + SYSCISCR ) ;
out :
2012-01-10 12:44:39 +04:00
spin_unlock_irqrestore ( & r8a7779_sysc_lock , flags ) ;
2012-01-10 10:50:01 +04:00
pr_debug ( " r8a7779 power domain %d: %02x %02x %02x %02x %02x -> %d \n " ,
r8a7779_ch - > isr_bit , ioread32 ( r8a7779_sysc_base + PWRSR0 ) ,
ioread32 ( r8a7779_sysc_base + PWRSR1 ) ,
ioread32 ( r8a7779_sysc_base + PWRSR2 ) ,
ioread32 ( r8a7779_sysc_base + PWRSR3 ) ,
ioread32 ( r8a7779_sysc_base + PWRSR4 ) , ret ) ;
return ret ;
}
2012-01-10 12:44:39 +04:00
int r8a7779_sysc_power_down ( struct r8a7779_pm_ch * r8a7779_ch )
2012-01-10 10:50:01 +04:00
{
return r8a7779_sysc_update ( r8a7779_ch , r8a7779_sysc_pwr_off ) ;
}
2012-01-10 12:44:39 +04:00
int r8a7779_sysc_power_up ( struct r8a7779_pm_ch * r8a7779_ch )
2012-01-10 10:50:01 +04:00
{
return r8a7779_sysc_update ( r8a7779_ch , r8a7779_sysc_pwr_on ) ;
}
static void __init r8a7779_sysc_init ( void )
{
r8a7779_sysc_base = ioremap_nocache ( 0xffd85000 , PAGE_SIZE ) ;
if ( ! r8a7779_sysc_base )
panic ( " unable to ioremap r8a7779 SYSC hardware block \n " ) ;
/* enable all interrupt sources, but do not use interrupt handler */
iowrite32 ( 0x0131000e , r8a7779_sysc_base + SYSCIER ) ;
iowrite32 ( 0 , r8a7779_sysc_base + SYSCIMR ) ;
}
2012-01-10 12:44:39 +04:00
# else /* CONFIG_PM || CONFIG_SMP */
static inline void r8a7779_sysc_init ( void ) { }
# endif /* CONFIG_PM || CONFIG_SMP */
# ifdef CONFIG_PM
2012-01-10 10:50:01 +04:00
static int pd_power_down ( struct generic_pm_domain * genpd )
{
return r8a7779_sysc_power_down ( to_r8a7779_ch ( genpd ) ) ;
}
static int pd_power_up ( struct generic_pm_domain * genpd )
{
return r8a7779_sysc_power_up ( to_r8a7779_ch ( genpd ) ) ;
}
static bool pd_is_off ( struct generic_pm_domain * genpd )
{
struct r8a7779_pm_ch * r8a7779_ch = to_r8a7779_ch ( genpd ) ;
unsigned int st ;
st = ioread32 ( r8a7779_sysc_base + r8a7779_ch - > chan_offs + PWRSR_OFFS ) ;
if ( st & ( 1 < < r8a7779_ch - > chan_bit ) )
return true ;
return false ;
}
static bool pd_active_wakeup ( struct device * dev )
{
return true ;
}
2012-08-07 03:14:14 +04:00
static void r8a7779_init_pm_domain ( struct r8a7779_pm_domain * r8a7779_pd )
2012-01-10 10:50:01 +04:00
{
struct generic_pm_domain * genpd = & r8a7779_pd - > genpd ;
pm_genpd_init ( genpd , NULL , false ) ;
genpd - > dev_ops . stop = pm_clk_suspend ;
genpd - > dev_ops . start = pm_clk_resume ;
genpd - > dev_ops . active_wakeup = pd_active_wakeup ;
genpd - > dev_irq_safe = true ;
genpd - > power_off = pd_power_down ;
genpd - > power_on = pd_power_up ;
if ( pd_is_off ( & r8a7779_pd - > genpd ) )
pd_power_up ( & r8a7779_pd - > genpd ) ;
}
2012-08-07 03:14:14 +04:00
static struct r8a7779_pm_domain r8a7779_pm_domains [ ] = {
{
. genpd . name = " SH4A " ,
. ch = {
. chan_offs = 0x80 , /* PWRSR1 .. PWRER1 */
. isr_bit = 16 , /* SH4A */
} ,
} ,
{
. genpd . name = " SGX " ,
. ch = {
. chan_offs = 0xc0 , /* PWRSR2 .. PWRER2 */
. isr_bit = 20 , /* SGX */
} ,
} ,
{
. genpd . name = " VDP1 " ,
. ch = {
. chan_offs = 0x100 , /* PWRSR3 .. PWRER3 */
. isr_bit = 21 , /* VDP */
} ,
} ,
{
. genpd . name = " IMPX3 " ,
. ch = {
. chan_offs = 0x140 , /* PWRSR4 .. PWRER4 */
. isr_bit = 24 , /* IMP */
} ,
} ,
2012-01-10 10:50:01 +04:00
} ;
2012-08-07 03:14:14 +04:00
void __init r8a7779_init_pm_domains ( void )
{
int j ;
2012-01-10 10:50:01 +04:00
2012-08-07 03:14:14 +04:00
for ( j = 0 ; j < ARRAY_SIZE ( r8a7779_pm_domains ) ; j + + )
r8a7779_init_pm_domain ( & r8a7779_pm_domains [ j ] ) ;
}
2012-01-10 10:50:01 +04:00
# endif /* CONFIG_PM */
void __init r8a7779_pm_init ( void )
{
2012-01-10 12:44:39 +04:00
static int once ;
if ( ! once + + )
r8a7779_sysc_init ( ) ;
2012-01-10 10:50:01 +04:00
}