2010-08-02 14:18:18 +04:00
/*
* OMAP4 Power Management Routines
*
* Copyright ( C ) 2010 Texas Instruments , Inc .
* Rajendra Nayak < rnayak @ ti . com >
*
* 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/pm.h>
# include <linux/suspend.h>
# include <linux/module.h>
# include <linux/list.h>
# include <linux/err.h>
# include <linux/slab.h>
2011-11-11 01:45:17 +04:00
# include "common.h"
2011-01-05 19:33:17 +03:00
# include "clockdomain.h"
2010-12-22 07:05:16 +03:00
# include "powerdomain.h"
2010-08-02 14:18:18 +04:00
struct power_state {
struct powerdomain * pwrdm ;
u32 next_state ;
# ifdef CONFIG_SUSPEND
u32 saved_state ;
# endif
struct list_head node ;
} ;
static LIST_HEAD ( pwrst_list ) ;
# ifdef CONFIG_SUSPEND
static int omap4_pm_suspend ( void )
{
do_wfi ( ) ;
return 0 ;
}
static int omap4_pm_enter ( suspend_state_t suspend_state )
{
int ret = 0 ;
switch ( suspend_state ) {
case PM_SUSPEND_STANDBY :
case PM_SUSPEND_MEM :
ret = omap4_pm_suspend ( ) ;
break ;
default :
ret = - EINVAL ;
}
return ret ;
}
static int omap4_pm_begin ( suspend_state_t state )
{
2010-12-09 20:39:58 +03:00
disable_hlt ( ) ;
2010-08-02 14:18:18 +04:00
return 0 ;
}
static void omap4_pm_end ( void )
{
2010-12-09 20:39:58 +03:00
enable_hlt ( ) ;
2010-08-02 14:18:18 +04:00
return ;
}
2010-11-16 16:14:02 +03:00
static const struct platform_suspend_ops omap_pm_ops = {
2010-08-02 14:18:18 +04:00
. begin = omap4_pm_begin ,
. end = omap4_pm_end ,
. enter = omap4_pm_enter ,
. valid = suspend_valid_only_mem ,
} ;
# endif /* CONFIG_SUSPEND */
2011-01-05 19:33:17 +03:00
/*
* Enable hardware supervised mode for all clockdomains if it ' s
* supported . Initiate sleep transition for other clockdomains , if
* they are not used
*/
static int __init clkdms_setup ( struct clockdomain * clkdm , void * unused )
{
if ( clkdm - > flags & CLKDM_CAN_ENABLE_AUTO )
clkdm_allow_idle ( clkdm ) ;
else if ( clkdm - > flags & CLKDM_CAN_FORCE_SLEEP & &
atomic_read ( & clkdm - > usecount ) = = 0 )
clkdm_sleep ( clkdm ) ;
return 0 ;
}
2010-08-02 14:18:18 +04:00
static int __init pwrdms_setup ( struct powerdomain * pwrdm , void * unused )
{
struct power_state * pwrst ;
if ( ! pwrdm - > pwrsts )
return 0 ;
pwrst = kmalloc ( sizeof ( struct power_state ) , GFP_ATOMIC ) ;
if ( ! pwrst )
return - ENOMEM ;
pwrst - > pwrdm = pwrdm ;
pwrst - > next_state = PWRDM_POWER_ON ;
list_add ( & pwrst - > node , & pwrst_list ) ;
return pwrdm_set_next_pwrst ( pwrst - > pwrdm , pwrst - > next_state ) ;
}
/**
* omap4_pm_init - Init routine for OMAP4 PM
*
* Initializes all powerdomain and clockdomain target states
* and all PRCM settings .
*/
static int __init omap4_pm_init ( void )
{
int ret ;
2011-03-08 15:54:30 +03:00
struct clockdomain * emif_clkdm , * mpuss_clkdm , * l3_1_clkdm ;
struct clockdomain * ducati_clkdm , * l3_2_clkdm , * l4_per_clkdm ;
2010-08-02 14:18:18 +04:00
if ( ! cpu_is_omap44xx ( ) )
return - ENODEV ;
2011-03-11 13:43:09 +03:00
if ( omap_rev ( ) = = OMAP4430_REV_ES1_0 ) {
WARN ( 1 , " Power Management not supported on OMAP4430 ES1.0 \n " ) ;
return - ENODEV ;
}
2010-08-02 14:18:18 +04:00
pr_err ( " Power Management for TI OMAP4. \n " ) ;
ret = pwrdm_for_each ( pwrdms_setup , NULL ) ;
if ( ret ) {
pr_err ( " Failed to setup powerdomains \n " ) ;
goto err2 ;
}
2011-03-08 15:54:30 +03:00
/*
* The dynamic dependency between MPUSS - > MEMIF and
* MPUSS - > L4_PER / L3_ * and DUCATI - > L3_ * doesn ' t work as
* expected . The hardware recommendation is to enable static
* dependencies for these to avoid system lock ups or random crashes .
*/
mpuss_clkdm = clkdm_lookup ( " mpuss_clkdm " ) ;
emif_clkdm = clkdm_lookup ( " l3_emif_clkdm " ) ;
l3_1_clkdm = clkdm_lookup ( " l3_1_clkdm " ) ;
l3_2_clkdm = clkdm_lookup ( " l3_2_clkdm " ) ;
l4_per_clkdm = clkdm_lookup ( " l4_per_clkdm " ) ;
ducati_clkdm = clkdm_lookup ( " ducati_clkdm " ) ;
if ( ( ! mpuss_clkdm ) | | ( ! emif_clkdm ) | | ( ! l3_1_clkdm ) | |
( ! l3_2_clkdm ) | | ( ! ducati_clkdm ) | | ( ! l4_per_clkdm ) )
goto err2 ;
ret = clkdm_add_wkdep ( mpuss_clkdm , emif_clkdm ) ;
ret | = clkdm_add_wkdep ( mpuss_clkdm , l3_1_clkdm ) ;
ret | = clkdm_add_wkdep ( mpuss_clkdm , l3_2_clkdm ) ;
ret | = clkdm_add_wkdep ( mpuss_clkdm , l4_per_clkdm ) ;
ret | = clkdm_add_wkdep ( ducati_clkdm , l3_1_clkdm ) ;
ret | = clkdm_add_wkdep ( ducati_clkdm , l3_2_clkdm ) ;
if ( ret ) {
pr_err ( " Failed to add MPUSS -> L3/EMIF/L4PER, DUCATI -> L3 "
" wakeup dependency \n " ) ;
goto err2 ;
}
2010-06-16 20:49:48 +04:00
ret = omap4_mpuss_init ( ) ;
if ( ret ) {
pr_err ( " Failed to initialise OMAP4 MPUSS \n " ) ;
goto err2 ;
}
2011-01-05 19:33:17 +03:00
( void ) clkdm_for_each ( clkdms_setup , NULL ) ;
2010-08-02 14:18:18 +04:00
# ifdef CONFIG_SUSPEND
suspend_set_ops ( & omap_pm_ops ) ;
# endif /* CONFIG_SUSPEND */
err2 :
return ret ;
}
late_initcall ( omap4_pm_init ) ;