2014-01-15 16:43:08 +09:00
/*
* R - Car SYSC Power management support
*
* Copyright ( C ) 2014 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/delay.h>
# include <linux/err.h>
# include <linux/mm.h>
# include <linux/spinlock.h>
2015-08-10 23:07:05 -04:00
# include <linux/io.h>
2014-06-17 16:47:53 +09:00
# include "pm-rcar.h"
2014-01-15 16:43:08 +09:00
2015-06-04 20:22:27 +02:00
/* SYSC Common */
# define SYSCSR 0x00 /* SYSC Status Register */
# define SYSCISR 0x04 /* Interrupt Status Register */
# define SYSCISCR 0x08 /* Interrupt Status Clear Register */
# define SYSCIER 0x0c /* Interrupt Enable Register */
# define SYSCIMR 0x10 /* Interrupt Mask Register */
2014-01-15 16:43:08 +09:00
2015-06-04 20:22:27 +02:00
/* SYSC Status Register */
# define SYSCSR_PONENB 1 /* Ready for power resume requests */
# define SYSCSR_POFFENB 0 /* Ready for power shutoff requests */
2014-01-15 16:43:08 +09:00
2015-06-04 20:22:27 +02:00
/*
* Power Control Register Offsets inside the register block for each domain
* Note : The " CR " registers for ARM cores exist on H1 only
* Use WFI to power off , CPG / APMU to resume ARM cores on R - Car Gen2
*/
# define PWRSR_OFFS 0x00 /* Power Status Register */
# define PWROFFCR_OFFS 0x04 /* Power Shutoff Control Register */
# define PWROFFSR_OFFS 0x08 /* Power Shutoff Status Register */
# define PWRONCR_OFFS 0x0c /* Power Resume Control Register */
# define PWRONSR_OFFS 0x10 /* Power Resume Status Register */
# define PWRER_OFFS 0x14 /* Power Shutoff/Resume Error */
# define SYSCSR_RETRIES 100
# define SYSCSR_DELAY_US 1
2014-01-15 16:43:08 +09:00
2015-06-04 20:22:29 +02:00
# define PWRER_RETRIES 100
# define PWRER_DELAY_US 1
2015-06-04 20:22:27 +02:00
# define SYSCISR_RETRIES 1000
# define SYSCISR_DELAY_US 1
2014-01-15 16:43:08 +09:00
2014-02-24 14:52:12 +09:00
static void __iomem * rcar_sysc_base ;
2014-01-15 16:43:08 +09:00
static DEFINE_SPINLOCK ( rcar_sysc_lock ) ; /* SMP CPUs + I/O devices */
2015-06-04 20:22:32 +02:00
static int rcar_sysc_pwr_on_off ( const struct rcar_sysc_ch * sysc_ch , bool on )
2014-01-15 16:43:08 +09:00
{
2015-06-04 20:22:32 +02:00
unsigned int sr_bit , reg_offs ;
2014-01-15 16:43:08 +09:00
int k ;
2015-06-04 20:22:32 +02:00
if ( on ) {
sr_bit = SYSCSR_PONENB ;
reg_offs = PWRONCR_OFFS ;
} else {
sr_bit = SYSCSR_POFFENB ;
reg_offs = PWROFFCR_OFFS ;
}
2015-06-04 20:22:27 +02:00
/* Wait until SYSC is ready to accept a power request */
2014-01-15 16:43:08 +09:00
for ( k = 0 ; k < SYSCSR_RETRIES ; k + + ) {
2015-06-04 20:22:31 +02:00
if ( ioread32 ( rcar_sysc_base + SYSCSR ) & BIT ( sr_bit ) )
2014-01-15 16:43:08 +09:00
break ;
udelay ( SYSCSR_DELAY_US ) ;
}
if ( k = = SYSCSR_RETRIES )
return - EAGAIN ;
2015-06-04 20:22:27 +02:00
/* Submit power shutoff or power resume request */
2015-06-04 20:22:31 +02:00
iowrite32 ( BIT ( sysc_ch - > chan_bit ) ,
2014-01-15 16:43:08 +09:00
rcar_sysc_base + sysc_ch - > chan_offs + reg_offs ) ;
return 0 ;
}
2015-06-04 20:22:32 +02:00
static int rcar_sysc_power ( const struct rcar_sysc_ch * sysc_ch , bool on )
2014-01-15 16:43:08 +09:00
{
2015-06-04 20:22:31 +02:00
unsigned int isr_mask = BIT ( sysc_ch - > isr_bit ) ;
unsigned int chan_mask = BIT ( sysc_ch - > chan_bit ) ;
2014-01-15 16:43:08 +09:00
unsigned int status ;
unsigned long flags ;
int ret = 0 ;
int k ;
spin_lock_irqsave ( & rcar_sysc_lock , flags ) ;
iowrite32 ( isr_mask , rcar_sysc_base + SYSCISCR ) ;
2015-06-04 20:22:27 +02:00
/* Submit power shutoff or resume request until it was accepted */
2015-06-04 20:22:29 +02:00
for ( k = 0 ; k < PWRER_RETRIES ; k + + ) {
2015-06-04 20:22:32 +02:00
ret = rcar_sysc_pwr_on_off ( sysc_ch , on ) ;
2014-01-15 16:43:08 +09:00
if ( ret )
goto out ;
status = ioread32 ( rcar_sysc_base +
sysc_ch - > chan_offs + PWRER_OFFS ) ;
2015-06-04 20:22:29 +02:00
if ( ! ( status & chan_mask ) )
break ;
udelay ( PWRER_DELAY_US ) ;
}
if ( k = = PWRER_RETRIES ) {
ret = - EIO ;
goto out ;
}
2014-01-15 16:43:08 +09:00
2015-06-04 20:22:27 +02:00
/* Wait until the power shutoff or resume request has completed * */
2014-01-15 16:43:08 +09:00
for ( k = 0 ; k < SYSCISR_RETRIES ; k + + ) {
if ( ioread32 ( rcar_sysc_base + SYSCISR ) & isr_mask )
break ;
udelay ( SYSCISR_DELAY_US ) ;
}
if ( k = = SYSCISR_RETRIES )
ret = - EIO ;
iowrite32 ( isr_mask , rcar_sysc_base + SYSCISCR ) ;
out :
spin_unlock_irqrestore ( & rcar_sysc_lock , flags ) ;
pr_debug ( " sysc power domain %d: %08x -> %d \n " ,
sysc_ch - > isr_bit , ioread32 ( rcar_sysc_base + SYSCISR ) , ret ) ;
return ret ;
}
2015-06-04 20:22:30 +02:00
int rcar_sysc_power_down ( const struct rcar_sysc_ch * sysc_ch )
2014-01-15 16:43:08 +09:00
{
2015-06-04 20:22:32 +02:00
return rcar_sysc_power ( sysc_ch , false ) ;
2014-01-15 16:43:08 +09:00
}
2015-06-04 20:22:30 +02:00
int rcar_sysc_power_up ( const struct rcar_sysc_ch * sysc_ch )
2014-01-15 16:43:08 +09:00
{
2015-06-04 20:22:32 +02:00
return rcar_sysc_power ( sysc_ch , true ) ;
2014-01-15 16:43:08 +09:00
}
2015-06-04 20:22:30 +02:00
bool rcar_sysc_power_is_off ( const struct rcar_sysc_ch * sysc_ch )
2014-01-15 16:43:08 +09:00
{
unsigned int st ;
st = ioread32 ( rcar_sysc_base + sysc_ch - > chan_offs + PWRSR_OFFS ) ;
2015-06-04 20:22:31 +02:00
if ( st & BIT ( sysc_ch - > chan_bit ) )
2014-01-15 16:43:08 +09:00
return true ;
return false ;
}
void __iomem * rcar_sysc_init ( phys_addr_t base )
{
rcar_sysc_base = ioremap_nocache ( base , PAGE_SIZE ) ;
if ( ! rcar_sysc_base )
panic ( " unable to ioremap R-Car SYSC hardware block \n " ) ;
return rcar_sysc_base ;
}