2010-07-27 02:34:31 +04:00
/*
* pm . c - Common OMAP2 + power management - related code
*
* Copyright ( C ) 2010 Texas Instruments , Inc .
* Copyright ( C ) 2010 Nokia 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/kernel.h>
# include <linux/init.h>
# include <linux/io.h>
# include <linux/err.h>
2010-05-29 20:32:25 +04:00
# include <linux/opp.h>
2011-08-01 00:17:29 +04:00
# include <linux/export.h>
2010-07-27 02:34:31 +04:00
# include <plat/omap-pm.h>
# include <plat/omap_device.h>
2011-11-11 01:45:17 +04:00
# include "common.h"
2010-07-27 02:34:31 +04:00
2011-02-26 01:54:33 +03:00
# include "voltage.h"
2010-12-22 07:05:16 +03:00
# include "powerdomain.h"
2010-12-22 07:05:15 +03:00
# include "clockdomain.h"
2010-05-29 20:32:23 +04:00
# include "pm.h"
2011-11-24 02:43:01 +04:00
# include "twl-common.h"
2010-09-14 23:34:01 +04:00
2010-07-27 02:34:31 +04:00
static struct omap_device_pm_latency * pm_lats ;
2012-02-20 21:43:30 +04:00
static int __init _init_omap_device ( char * name )
2010-07-27 02:34:31 +04:00
{
struct omap_hwmod * oh ;
2011-07-22 00:48:45 +04:00
struct platform_device * pdev ;
2010-07-27 02:34:31 +04:00
oh = omap_hwmod_lookup ( name ) ;
if ( WARN ( ! oh , " %s: could not find omap_hwmod for %s \n " ,
__func__ , name ) )
return - ENODEV ;
2011-07-22 00:48:45 +04:00
pdev = omap_device_build ( oh - > name , 0 , oh , NULL , 0 , pm_lats , 0 , false ) ;
if ( WARN ( IS_ERR ( pdev ) , " %s: could not build omap_device for %s \n " ,
2010-07-27 02:34:31 +04:00
__func__ , name ) )
return - ENODEV ;
return 0 ;
}
/*
* Build omap_devices for processors and bus .
*/
static void omap2_init_processor_devices ( void )
{
2011-08-16 17:03:59 +04:00
_init_omap_device ( " mpu " ) ;
2011-02-25 16:27:20 +03:00
if ( omap3_has_iva ( ) )
2011-08-16 17:03:59 +04:00
_init_omap_device ( " iva " ) ;
2011-02-25 16:27:20 +03:00
2010-08-05 17:22:35 +04:00
if ( cpu_is_omap44xx ( ) ) {
2011-08-16 17:03:59 +04:00
_init_omap_device ( " l3_main_1 " ) ;
_init_omap_device ( " dsp " ) ;
_init_omap_device ( " iva " ) ;
2010-08-05 17:22:35 +04:00
} else {
2011-08-16 17:03:59 +04:00
_init_omap_device ( " l3_main " ) ;
2010-08-05 17:22:35 +04:00
}
2010-07-27 02:34:31 +04:00
}
2010-12-22 08:37:27 +03:00
/* Types of sleep_switch used in omap_set_pwrdm_state */
# define FORCEWAKEUP_SWITCH 0
# define LOWPOWERSTATE_SWITCH 1
2012-02-02 13:38:50 +04:00
int __init omap_pm_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-09-14 23:34:01 +04:00
/*
* This sets pwrdm state ( other than mpu & core . Currently only ON &
2010-12-22 08:37:28 +03:00
* RET are supported .
2010-09-14 23:34:01 +04:00
*/
2012-01-30 13:47:24 +04:00
int omap_set_pwrdm_state ( struct powerdomain * pwrdm , u32 pwrst )
2010-09-14 23:34:01 +04:00
{
2012-01-30 13:47:24 +04:00
u8 curr_pwrst , next_pwrst ;
int sleep_switch = - 1 , ret = 0 , hwsup = 0 ;
2010-09-14 23:34:01 +04:00
2012-01-30 13:47:24 +04:00
if ( ! pwrdm | | IS_ERR ( pwrdm ) )
2010-09-14 23:34:01 +04:00
return - EINVAL ;
2012-01-30 13:47:24 +04:00
while ( ! ( pwrdm - > pwrsts & ( 1 < < pwrst ) ) ) {
if ( pwrst = = PWRDM_POWER_OFF )
2010-09-14 23:34:01 +04:00
return ret ;
2012-01-30 13:47:24 +04:00
pwrst - - ;
2010-09-14 23:34:01 +04:00
}
2012-01-30 13:47:24 +04:00
next_pwrst = pwrdm_read_next_pwrst ( pwrdm ) ;
if ( next_pwrst = = pwrst )
2010-09-14 23:34:01 +04:00
return ret ;
2012-01-30 13:47:24 +04:00
curr_pwrst = pwrdm_read_pwrst ( pwrdm ) ;
if ( curr_pwrst < PWRDM_POWER_ON ) {
if ( ( curr_pwrst > pwrst ) & &
2010-12-22 08:37:27 +03:00
( pwrdm - > flags & PWRDM_HAS_LOWPOWERSTATECHANGE ) ) {
sleep_switch = LOWPOWERSTATE_SWITCH ;
} else {
2011-07-10 15:56:54 +04:00
hwsup = clkdm_in_hwsup ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2011-02-26 02:06:47 +03:00
clkdm_wakeup ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-22 08:37:27 +03:00
sleep_switch = FORCEWAKEUP_SWITCH ;
}
2010-09-14 23:34:01 +04:00
}
2012-01-30 13:47:24 +04:00
ret = pwrdm_set_next_pwrst ( pwrdm , pwrst ) ;
if ( ret )
pr_err ( " %s: unable to set power state of powerdomain: %s \n " ,
2011-08-30 20:48:17 +04:00
__func__ , pwrdm - > name ) ;
2010-09-14 23:34:01 +04:00
2010-12-22 08:37:27 +03:00
switch ( sleep_switch ) {
case FORCEWAKEUP_SWITCH :
2011-07-10 15:56:54 +04:00
if ( hwsup )
2011-02-26 02:06:48 +03:00
clkdm_allow_idle ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-22 08:37:28 +03:00
else
2011-02-26 02:06:47 +03:00
clkdm_sleep ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-22 08:37:27 +03:00
break ;
case LOWPOWERSTATE_SWITCH :
pwrdm_set_lowpwrstchange ( pwrdm ) ;
2012-01-30 13:47:24 +04:00
pwrdm_wait_transition ( pwrdm ) ;
pwrdm_state_switch ( pwrdm ) ;
2010-12-22 08:37:27 +03:00
break ;
2010-09-14 23:34:01 +04:00
}
return ret ;
}
2010-05-29 20:32:25 +04:00
/*
2011-08-30 20:48:16 +04:00
* This API is to be called during init to set the various voltage
2010-05-29 20:32:25 +04:00
* domains to the voltage as per the opp table . Typically we boot up
* at the nominal voltage . So this function finds out the rate of
* the clock associated with the voltage domain , finds out the correct
2011-08-30 20:48:16 +04:00
* opp entry and sets the voltage domain to the voltage specified
2010-05-29 20:32:25 +04:00
* in the opp entry
*/
static int __init omap2_set_init_voltage ( char * vdd_name , char * clk_name ,
2011-08-16 17:02:20 +04:00
const char * oh_name )
2010-05-29 20:32:25 +04:00
{
struct voltagedomain * voltdm ;
struct clk * clk ;
struct opp * opp ;
unsigned long freq , bootup_volt ;
2011-08-16 17:02:20 +04:00
struct device * dev ;
2010-05-29 20:32:25 +04:00
2011-08-16 17:02:20 +04:00
if ( ! vdd_name | | ! clk_name | | ! oh_name ) {
2011-08-30 20:48:17 +04:00
pr_err ( " %s: invalid parameters \n " , __func__ ) ;
2010-05-29 20:32:25 +04:00
goto exit ;
}
2011-08-16 17:02:20 +04:00
dev = omap_device_get_by_hwmod_name ( oh_name ) ;
if ( IS_ERR ( dev ) ) {
pr_err ( " %s: Unable to get dev pointer for hwmod %s \n " ,
__func__ , oh_name ) ;
goto exit ;
}
2011-03-17 00:25:45 +03:00
voltdm = voltdm_lookup ( vdd_name ) ;
2010-05-29 20:32:25 +04:00
if ( IS_ERR ( voltdm ) ) {
2011-08-30 20:48:17 +04:00
pr_err ( " %s: unable to get vdd pointer for vdd_%s \n " ,
2010-05-29 20:32:25 +04:00
__func__ , vdd_name ) ;
goto exit ;
}
clk = clk_get ( NULL , clk_name ) ;
if ( IS_ERR ( clk ) ) {
2011-08-30 20:48:17 +04:00
pr_err ( " %s: unable to get clk %s \n " , __func__ , clk_name ) ;
2010-05-29 20:32:25 +04:00
goto exit ;
}
freq = clk - > rate ;
clk_put ( clk ) ;
opp = opp_find_freq_ceil ( dev , & freq ) ;
if ( IS_ERR ( opp ) ) {
2011-08-30 20:48:17 +04:00
pr_err ( " %s: unable to find boot up OPP for vdd_%s \n " ,
2010-05-29 20:32:25 +04:00
__func__ , vdd_name ) ;
goto exit ;
}
bootup_volt = opp_get_voltage ( opp ) ;
if ( ! bootup_volt ) {
2011-08-30 20:48:17 +04:00
pr_err ( " %s: unable to find voltage corresponding "
2010-05-29 20:32:25 +04:00
" to the bootup OPP for vdd_%s \n " , __func__ , vdd_name ) ;
goto exit ;
}
2011-04-06 03:27:21 +04:00
voltdm_scale ( voltdm , bootup_volt ) ;
2010-05-29 20:32:25 +04:00
return 0 ;
exit :
2011-08-30 20:48:17 +04:00
pr_err ( " %s: unable to set vdd_%s \n " , __func__ , vdd_name ) ;
2010-05-29 20:32:25 +04:00
return - EINVAL ;
}
static void __init omap3_init_voltages ( void )
{
if ( ! cpu_is_omap34xx ( ) )
return ;
2011-08-16 17:02:20 +04:00
omap2_set_init_voltage ( " mpu_iva " , " dpll1_ck " , " mpu " ) ;
omap2_set_init_voltage ( " core " , " l3_ick " , " l3_main " ) ;
2010-05-29 20:32:25 +04:00
}
2010-05-29 20:32:25 +04:00
static void __init omap4_init_voltages ( void )
{
if ( ! cpu_is_omap44xx ( ) )
return ;
2011-08-16 17:02:20 +04:00
omap2_set_init_voltage ( " mpu " , " dpll_mpu_ck " , " mpu " ) ;
omap2_set_init_voltage ( " core " , " l3_div_ck " , " l3_main_1 " ) ;
omap2_set_init_voltage ( " iva " , " dpll_iva_m5x2_ck " , " iva " ) ;
2010-05-29 20:32:25 +04:00
}
2010-07-27 02:34:31 +04:00
static int __init omap2_common_pm_init ( void )
{
2011-08-16 13:49:08 +04:00
if ( ! of_have_populated_dt ( ) )
omap2_init_processor_devices ( ) ;
2010-07-27 02:34:31 +04:00
omap_pm_if_init ( ) ;
return 0 ;
}
2010-12-20 18:47:21 +03:00
postcore_initcall ( omap2_common_pm_init ) ;
2010-07-27 02:34:31 +04:00
2010-05-29 20:32:21 +04:00
static int __init omap2_common_pm_late_init ( void )
{
2010-12-10 20:21:05 +03:00
/* Init the voltage layer */
2011-11-24 02:43:01 +04:00
omap_pmic_late_init ( ) ;
2010-05-29 20:32:21 +04:00
omap_voltage_late_init ( ) ;
2010-05-29 20:32:25 +04:00
/* Initialize the voltages */
omap3_init_voltages ( ) ;
2010-05-29 20:32:25 +04:00
omap4_init_voltages ( ) ;
2010-05-29 20:32:25 +04:00
2010-12-10 20:21:05 +03:00
/* Smartreflex device init */
2010-05-29 20:32:23 +04:00
omap_devinit_smartreflex ( ) ;
2010-05-29 20:32:21 +04:00
return 0 ;
}
late_initcall ( omap2_common_pm_late_init ) ;