2008-02-24 13:51:38 +01:00
/*
* AVR32 AP Power Management
*
* Copyright ( C ) 2008 Atmel Corporation
*
* This program is free software ; you can redistribute it and / or
* modify it under the terms of the GNU General Public License
* version 2 as published by the Free Software Foundation .
*/
# include <linux/io.h>
# include <linux/suspend.h>
# include <linux/vmalloc.h>
# include <asm/cacheflush.h>
# include <asm/sysreg.h>
2008-07-31 16:06:58 +02:00
# include <mach/chip.h>
2008-08-05 13:57:38 +02:00
# include <mach/pm.h>
# include <mach/sram.h>
2008-02-24 13:51:38 +01:00
# include "sdramc.h"
# define SRAM_PAGE_FLAGS (SYSREG_BIT(TLBELO_D) | SYSREG_BF(SZ, 1) \
| SYSREG_BF ( AP , 3 ) | SYSREG_BIT ( G ) )
static unsigned long pm_sram_start ;
static size_t pm_sram_size ;
static struct vm_struct * pm_sram_area ;
static void ( * avr32_pm_enter_standby ) ( unsigned long sdramc_base ) ;
static void ( * avr32_pm_enter_str ) ( unsigned long sdramc_base ) ;
/*
* Must be called with interrupts disabled . Exceptions will be masked
* on return ( i . e . all exceptions will be " unrecoverable " . )
*/
static void * avr32_pm_map_sram ( void )
{
unsigned long vaddr ;
unsigned long page_addr ;
u32 tlbehi ;
u32 mmucr ;
vaddr = ( unsigned long ) pm_sram_area - > addr ;
page_addr = pm_sram_start & PAGE_MASK ;
/*
* Mask exceptions and grab the first TLB entry . We won ' t be
* needing it while sleeping .
*/
asm volatile ( " ssrf %0 " : : " i " ( SYSREG_EM_OFFSET ) : " memory " ) ;
mmucr = sysreg_read ( MMUCR ) ;
tlbehi = sysreg_read ( TLBEHI ) ;
sysreg_write ( MMUCR , SYSREG_BFINS ( DRP , 0 , mmucr ) ) ;
tlbehi = SYSREG_BF ( ASID , SYSREG_BFEXT ( ASID , tlbehi ) ) ;
tlbehi | = vaddr & PAGE_MASK ;
tlbehi | = SYSREG_BIT ( TLBEHI_V ) ;
sysreg_write ( TLBELO , page_addr | SRAM_PAGE_FLAGS ) ;
sysreg_write ( TLBEHI , tlbehi ) ;
__builtin_tlbw ( ) ;
return ( void * ) ( vaddr + pm_sram_start - page_addr ) ;
}
/*
* Must be called with interrupts disabled . Exceptions will be
* unmasked on return .
*/
static void avr32_pm_unmap_sram ( void )
{
u32 mmucr ;
u32 tlbehi ;
u32 tlbarlo ;
/* Going to update TLB entry at index 0 */
mmucr = sysreg_read ( MMUCR ) ;
tlbehi = sysreg_read ( TLBEHI ) ;
sysreg_write ( MMUCR , SYSREG_BFINS ( DRP , 0 , mmucr ) ) ;
/* Clear the "valid" bit */
tlbehi = SYSREG_BF ( ASID , SYSREG_BFEXT ( ASID , tlbehi ) ) ;
sysreg_write ( TLBEHI , tlbehi ) ;
/* Mark it as "not accessed" */
tlbarlo = sysreg_read ( TLBARLO ) ;
sysreg_write ( TLBARLO , tlbarlo | 0x80000000U ) ;
/* Update the TLB */
__builtin_tlbw ( ) ;
/* Unmask exceptions */
asm volatile ( " csrf %0 " : : " i " ( SYSREG_EM_OFFSET ) : " memory " ) ;
}
static int avr32_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 int avr32_pm_enter ( suspend_state_t state )
{
u32 lpr_saved ;
u32 evba_saved ;
void * sram ;
switch ( state ) {
case PM_SUSPEND_STANDBY :
sram = avr32_pm_map_sram ( ) ;
/* Switch to in-sram exception handlers */
evba_saved = sysreg_read ( EVBA ) ;
sysreg_write ( EVBA , ( unsigned long ) sram ) ;
/*
* Save the LPR register so that we can re - enable
* SDRAM Low Power mode on resume .
*/
lpr_saved = sdramc_readl ( LPR ) ;
pr_debug ( " %s: Entering standby... \n " , __func__ ) ;
avr32_pm_enter_standby ( SDRAMC_BASE ) ;
sdramc_writel ( LPR , lpr_saved ) ;
/* Switch back to regular exception handlers */
sysreg_write ( EVBA , evba_saved ) ;
avr32_pm_unmap_sram ( ) ;
break ;
case PM_SUSPEND_MEM :
sram = avr32_pm_map_sram ( ) ;
/* Switch to in-sram exception handlers */
evba_saved = sysreg_read ( EVBA ) ;
sysreg_write ( EVBA , ( unsigned long ) sram ) ;
/*
* Save the LPR register so that we can re - enable
* SDRAM Low Power mode on resume .
*/
lpr_saved = sdramc_readl ( LPR ) ;
pr_debug ( " %s: Entering suspend-to-ram... \n " , __func__ ) ;
avr32_pm_enter_str ( SDRAMC_BASE ) ;
sdramc_writel ( LPR , lpr_saved ) ;
/* Switch back to regular exception handlers */
sysreg_write ( EVBA , evba_saved ) ;
avr32_pm_unmap_sram ( ) ;
break ;
case PM_SUSPEND_ON :
pr_debug ( " %s: Entering idle... \n " , __func__ ) ;
cpu_enter_idle ( ) ;
break ;
default :
pr_debug ( " %s: Invalid suspend state %d \n " , __func__ , state ) ;
goto out ;
}
pr_debug ( " %s: wakeup \n " , __func__ ) ;
out :
return 0 ;
}
2010-11-16 14:14:02 +01:00
static const struct platform_suspend_ops avr32_pm_ops = {
2008-02-24 13:51:38 +01:00
. valid = avr32_pm_valid_state ,
. enter = avr32_pm_enter ,
} ;
static unsigned long avr32_pm_offset ( void * symbol )
{
extern u8 pm_exception [ ] ;
return ( unsigned long ) symbol - ( unsigned long ) pm_exception ;
}
static int __init avr32_pm_init ( void )
{
extern u8 pm_exception [ ] ;
extern u8 pm_irq0 [ ] ;
extern u8 pm_standby [ ] ;
extern u8 pm_suspend_to_ram [ ] ;
extern u8 pm_sram_end [ ] ;
void * dst ;
/*
* To keep things simple , we depend on not needing more than a
* single page .
*/
pm_sram_size = avr32_pm_offset ( pm_sram_end ) ;
if ( pm_sram_size > PAGE_SIZE )
goto err ;
pm_sram_start = sram_alloc ( pm_sram_size ) ;
if ( ! pm_sram_start )
goto err_alloc_sram ;
/* Grab a virtual area we can use later on. */
pm_sram_area = get_vm_area ( pm_sram_size , VM_IOREMAP ) ;
if ( ! pm_sram_area )
goto err_vm_area ;
pm_sram_area - > phys_addr = pm_sram_start ;
local_irq_disable ( ) ;
dst = avr32_pm_map_sram ( ) ;
memcpy ( dst , pm_exception , pm_sram_size ) ;
flush_dcache_region ( dst , pm_sram_size ) ;
invalidate_icache_region ( dst , pm_sram_size ) ;
avr32_pm_unmap_sram ( ) ;
local_irq_enable ( ) ;
avr32_pm_enter_standby = dst + avr32_pm_offset ( pm_standby ) ;
avr32_pm_enter_str = dst + avr32_pm_offset ( pm_suspend_to_ram ) ;
intc_set_suspend_handler ( avr32_pm_offset ( pm_irq0 ) ) ;
suspend_set_ops ( & avr32_pm_ops ) ;
printk ( " AVR32 AP Power Management enabled \n " ) ;
return 0 ;
err_vm_area :
sram_free ( pm_sram_start , pm_sram_size ) ;
err_alloc_sram :
err :
pr_err ( " AVR32 Power Management initialization failed \n " ) ;
return - ENOMEM ;
}
arch_initcall ( avr32_pm_init ) ;