diff options
Diffstat (limited to 'arch/arm/mach-omap2/pm.c')
| -rw-r--r-- | arch/arm/mach-omap2/pm.c | 144 | 
1 files changed, 132 insertions, 12 deletions
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 59ca03b0e69..d5a102c7198 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -13,13 +13,16 @@  #include <linux/init.h>  #include <linux/io.h>  #include <linux/err.h> +#include <linux/opp.h>  #include <plat/omap-pm.h>  #include <plat/omap_device.h>  #include <plat/common.h> +#include <plat/voltage.h> -#include <plat/powerdomain.h> -#include <plat/clockdomain.h> +#include "powerdomain.h" +#include "clockdomain.h" +#include "pm.h"  static struct omap_device_pm_latency *pm_lats; @@ -89,10 +92,13 @@ static void omap2_init_processor_devices(void)  	}  } +/* Types of sleep_switch used in omap_set_pwrdm_state */ +#define FORCEWAKEUP_SWITCH	0 +#define LOWPOWERSTATE_SWITCH	1 +  /*   * This sets pwrdm state (other than mpu & core. Currently only ON & - * RET are supported. Function is assuming that clkdm doesn't have - * hw_sup mode enabled. + * RET are supported.   */  int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)  { @@ -114,9 +120,14 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)  		return ret;  	if (pwrdm_read_pwrst(pwrdm) < PWRDM_POWER_ON) { -		omap2_clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); -		sleep_switch = 1; -		pwrdm_wait_transition(pwrdm); +		if ((pwrdm_read_pwrst(pwrdm) > state) && +			(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { +			sleep_switch = LOWPOWERSTATE_SWITCH; +		} else { +			omap2_clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); +			pwrdm_wait_transition(pwrdm); +			sleep_switch = FORCEWAKEUP_SWITCH; +		}  	}  	ret = pwrdm_set_next_pwrst(pwrdm, state); @@ -126,16 +137,106 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)  		goto err;  	} -	if (sleep_switch) { -		omap2_clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); -		pwrdm_wait_transition(pwrdm); -		pwrdm_state_switch(pwrdm); +	switch (sleep_switch) { +	case FORCEWAKEUP_SWITCH: +		if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO) +			omap2_clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); +		else +			omap2_clkdm_sleep(pwrdm->pwrdm_clkdms[0]); +		break; +	case LOWPOWERSTATE_SWITCH: +		pwrdm_set_lowpwrstchange(pwrdm); +		break; +	default: +		return ret;  	} +	pwrdm_wait_transition(pwrdm); +	pwrdm_state_switch(pwrdm);  err:  	return ret;  } +/* + * 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; +	} + +	voltdm = omap_voltage_domain_lookup(vdd_name); +	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; + +	omap2_set_init_voltage("mpu", "dpll1_ck", mpu_dev); +	omap2_set_init_voltage("core", "l3_ick", l3_dev); +} + +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); +} +  static int __init omap2_common_pm_init(void)  {  	omap2_init_processor_devices(); @@ -143,5 +244,24 @@ static int __init omap2_common_pm_init(void)  	return 0;  } -device_initcall(omap2_common_pm_init); +postcore_initcall(omap2_common_pm_init); + +static int __init omap2_common_pm_late_init(void) +{ +	/* Init the OMAP TWL parameters */ +	omap3_twl_init(); +	omap4_twl_init(); + +	/* Init the voltage layer */ +	omap_voltage_late_init(); +	/* Initialize the voltages */ +	omap3_init_voltages(); +	omap4_init_voltages(); + +	/* Smartreflex device init */ +	omap_devinit_smartreflex(); + +	return 0; +} +late_initcall(omap2_common_pm_late_init);  |