2010-07-26 16:34:31 -06: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 22:02:25 +05:30
# include <linux/opp.h>
2010-07-26 16:34:31 -06:00
# include <plat/omap-pm.h>
# include <plat/omap_device.h>
# include <plat/common.h>
2011-02-25 15:54:33 -07:00
# include "voltage.h"
2010-12-21 21:05:16 -07:00
# include "powerdomain.h"
2010-12-21 21:05:15 -07:00
# include "clockdomain.h"
2010-05-29 22:02:23 +05:30
# include "pm.h"
2010-09-15 01:04:01 +05:30
2010-07-26 16:34:31 -06:00
static struct omap_device_pm_latency * pm_lats ;
static struct device * mpu_dev ;
2010-09-01 13:44:53 +05:30
static struct device * iva_dev ;
2010-07-26 16:34:31 -06:00
static struct device * l3_dev ;
2010-09-01 13:44:53 +05:30
static struct device * dsp_dev ;
2010-07-26 16:34:31 -06:00
struct device * omap2_get_mpuss_device ( void )
{
WARN_ON_ONCE ( ! mpu_dev ) ;
return mpu_dev ;
}
2010-09-01 13:44:53 +05:30
struct device * omap2_get_iva_device ( void )
2010-07-26 16:34:31 -06:00
{
2010-09-01 13:44:53 +05:30
WARN_ON_ONCE ( ! iva_dev ) ;
return iva_dev ;
2010-07-26 16:34:31 -06:00
}
struct device * omap2_get_l3_device ( void )
{
WARN_ON_ONCE ( ! l3_dev ) ;
return l3_dev ;
}
2010-09-01 13:44:53 +05:30
struct device * omap4_get_dsp_device ( void )
{
WARN_ON_ONCE ( ! dsp_dev ) ;
return dsp_dev ;
}
EXPORT_SYMBOL ( omap4_get_dsp_device ) ;
2010-07-26 16:34:31 -06:00
/* static int _init_omap_device(struct omap_hwmod *oh, void *user) */
static int _init_omap_device ( char * name , struct device * * new_dev )
{
struct omap_hwmod * oh ;
struct omap_device * od ;
oh = omap_hwmod_lookup ( name ) ;
if ( WARN ( ! oh , " %s: could not find omap_hwmod for %s \n " ,
__func__ , name ) )
return - ENODEV ;
od = omap_device_build ( oh - > name , 0 , oh , NULL , 0 , pm_lats , 0 , false ) ;
if ( WARN ( IS_ERR ( od ) , " %s: could not build omap_device for %s \n " ,
__func__ , name ) )
return - ENODEV ;
* new_dev = & od - > pdev . dev ;
return 0 ;
}
/*
* Build omap_devices for processors and bus .
*/
static void omap2_init_processor_devices ( void )
{
_init_omap_device ( " mpu " , & mpu_dev ) ;
2011-02-25 18:57:20 +05:30
if ( omap3_has_iva ( ) )
_init_omap_device ( " iva " , & iva_dev ) ;
2010-08-05 15:22:35 +02:00
if ( cpu_is_omap44xx ( ) ) {
_init_omap_device ( " l3_main_1 " , & l3_dev ) ;
2010-09-01 13:44:53 +05:30
_init_omap_device ( " dsp " , & dsp_dev ) ;
2011-04-26 02:32:26 -07:00
_init_omap_device ( " iva " , & iva_dev ) ;
2010-08-05 15:22:35 +02:00
} else {
_init_omap_device ( " l3_main " , & l3_dev ) ;
}
2010-07-26 16:34:31 -06:00
}
2010-12-21 22:37:27 -07:00
/* Types of sleep_switch used in omap_set_pwrdm_state */
# define FORCEWAKEUP_SWITCH 0
# define LOWPOWERSTATE_SWITCH 1
2010-09-15 01:04:01 +05:30
/*
* This sets pwrdm state ( other than mpu & core . Currently only ON &
2010-12-21 22:37:28 -07:00
* RET are supported .
2010-09-15 01:04:01 +05:30
*/
int omap_set_pwrdm_state ( struct powerdomain * pwrdm , u32 state )
{
u32 cur_state ;
2011-07-09 20:42:11 -06:00
int sleep_switch = - 1 ;
2010-09-15 01:04:01 +05:30
int ret = 0 ;
2011-07-10 05:56:54 -06:00
int hwsup = 0 ;
2010-09-15 01:04:01 +05:30
if ( pwrdm = = NULL | | IS_ERR ( pwrdm ) )
return - EINVAL ;
while ( ! ( pwrdm - > pwrsts & ( 1 < < state ) ) ) {
if ( state = = PWRDM_POWER_OFF )
return ret ;
state - - ;
}
cur_state = pwrdm_read_next_pwrst ( pwrdm ) ;
if ( cur_state = = state )
return ret ;
if ( pwrdm_read_pwrst ( pwrdm ) < PWRDM_POWER_ON ) {
2010-12-21 22:37:27 -07:00
if ( ( pwrdm_read_pwrst ( pwrdm ) > state ) & &
( pwrdm - > flags & PWRDM_HAS_LOWPOWERSTATECHANGE ) ) {
sleep_switch = LOWPOWERSTATE_SWITCH ;
} else {
2011-07-10 05:56:54 -06:00
hwsup = clkdm_in_hwsup ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2011-02-25 16:06:47 -07:00
clkdm_wakeup ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-21 22:37:27 -07:00
sleep_switch = FORCEWAKEUP_SWITCH ;
}
2010-09-15 01:04:01 +05:30
}
ret = pwrdm_set_next_pwrst ( pwrdm , state ) ;
if ( ret ) {
printk ( KERN_ERR " Unable to set state of powerdomain: %s \n " ,
pwrdm - > name ) ;
goto err ;
}
2010-12-21 22:37:27 -07:00
switch ( sleep_switch ) {
case FORCEWAKEUP_SWITCH :
2011-07-10 05:56:54 -06:00
if ( hwsup )
2011-02-25 16:06:48 -07:00
clkdm_allow_idle ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-21 22:37:28 -07:00
else
2011-02-25 16:06:47 -07:00
clkdm_sleep ( pwrdm - > pwrdm_clkdms [ 0 ] ) ;
2010-12-21 22:37:27 -07:00
break ;
case LOWPOWERSTATE_SWITCH :
pwrdm_set_lowpwrstchange ( pwrdm ) ;
break ;
default :
return ret ;
2010-09-15 01:04:01 +05:30
}
2010-12-21 22:37:27 -07:00
pwrdm_state_switch ( pwrdm ) ;
2010-09-15 01:04:01 +05:30
err :
return ret ;
}
2010-05-29 22:02:25 +05:30
/*
* This API is to be called during init to put the various voltage
* 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
* opp entry and puts the voltage domain to the voltage specifies
* in the opp entry
*/
static int __init omap2_set_init_voltage ( char * vdd_name , char * clk_name ,
struct device * dev )
{
struct voltagedomain * voltdm ;
struct clk * clk ;
struct opp * opp ;
unsigned long freq , bootup_volt ;
if ( ! vdd_name | | ! clk_name | | ! dev ) {
printk ( KERN_ERR " %s: Invalid parameters! \n " , __func__ ) ;
goto exit ;
}
2011-03-16 14:25:45 -07:00
voltdm = voltdm_lookup ( vdd_name ) ;
2010-05-29 22:02:25 +05:30
if ( IS_ERR ( voltdm ) ) {
printk ( KERN_ERR " %s: Unable to get vdd pointer for vdd_%s \n " ,
__func__ , vdd_name ) ;
goto exit ;
}
clk = clk_get ( NULL , clk_name ) ;
if ( IS_ERR ( clk ) ) {
printk ( KERN_ERR " %s: unable to get clk %s \n " ,
__func__ , clk_name ) ;
goto exit ;
}
freq = clk - > rate ;
clk_put ( clk ) ;
opp = opp_find_freq_ceil ( dev , & freq ) ;
if ( IS_ERR ( opp ) ) {
printk ( KERN_ERR " %s: unable to find boot up OPP for vdd_%s \n " ,
__func__ , vdd_name ) ;
goto exit ;
}
bootup_volt = opp_get_voltage ( opp ) ;
if ( ! bootup_volt ) {
printk ( KERN_ERR " %s: unable to find voltage corresponding "
" to the bootup OPP for vdd_%s \n " , __func__ , vdd_name ) ;
goto exit ;
}
omap_voltage_scale_vdd ( voltdm , bootup_volt ) ;
return 0 ;
exit :
printk ( KERN_ERR " %s: Unable to put vdd_%s to its init voltage \n \n " ,
__func__ , vdd_name ) ;
return - EINVAL ;
}
static void __init omap3_init_voltages ( void )
{
if ( ! cpu_is_omap34xx ( ) )
return ;
2011-03-23 11:18:08 -07:00
omap2_set_init_voltage ( " mpu_iva " , " dpll1_ck " , mpu_dev ) ;
2010-05-29 22:02:25 +05:30
omap2_set_init_voltage ( " core " , " l3_ick " , l3_dev ) ;
}
2010-05-29 22:02:25 +05:30
static void __init omap4_init_voltages ( void )
{
if ( ! cpu_is_omap44xx ( ) )
return ;
omap2_set_init_voltage ( " mpu " , " dpll_mpu_ck " , mpu_dev ) ;
omap2_set_init_voltage ( " core " , " l3_div_ck " , l3_dev ) ;
omap2_set_init_voltage ( " iva " , " dpll_iva_m5x2_ck " , iva_dev ) ;
}
2010-07-26 16:34:31 -06:00
static int __init omap2_common_pm_init ( void )
{
omap2_init_processor_devices ( ) ;
omap_pm_if_init ( ) ;
return 0 ;
}
2010-12-20 21:17:21 +05:30
postcore_initcall ( omap2_common_pm_init ) ;
2010-07-26 16:34:31 -06:00
2010-05-29 22:02:21 +05:30
static int __init omap2_common_pm_late_init ( void )
{
2010-12-10 22:51:05 +05:30
/* Init the OMAP TWL parameters */
omap3_twl_init ( ) ;
2010-12-10 23:15:16 +05:30
omap4_twl_init ( ) ;
2010-05-29 22:02:25 +05:30
2010-12-10 22:51:05 +05:30
/* Init the voltage layer */
2010-05-29 22:02:21 +05:30
omap_voltage_late_init ( ) ;
2010-05-29 22:02:25 +05:30
/* Initialize the voltages */
omap3_init_voltages ( ) ;
2010-05-29 22:02:25 +05:30
omap4_init_voltages ( ) ;
2010-05-29 22:02:25 +05:30
2010-12-10 22:51:05 +05:30
/* Smartreflex device init */
2010-05-29 22:02:23 +05:30
omap_devinit_smartreflex ( ) ;
2010-05-29 22:02:21 +05:30
return 0 ;
}
late_initcall ( omap2_common_pm_late_init ) ;