diff options
Diffstat (limited to 'arch/arm/mach-omap2/pm.c')
| -rw-r--r-- | arch/arm/mach-omap2/pm.c | 238 |
1 files changed, 158 insertions, 80 deletions
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c index 1881fe91514..828aee9ea6a 100644 --- a/arch/arm/mach-omap2/pm.c +++ b/arch/arm/mach-omap2/pm.c @@ -13,22 +13,66 @@ #include <linux/init.h> #include <linux/io.h> #include <linux/err.h> -#include <linux/opp.h> +#include <linux/pm_opp.h> #include <linux/export.h> +#include <linux/suspend.h> +#include <linux/cpu.h> -#include <plat/omap-pm.h> -#include <plat/omap_device.h> +#include <asm/system_misc.h> + +#include "omap-pm.h" +#include "omap_device.h" #include "common.h" +#include "soc.h" +#include "prcm-common.h" #include "voltage.h" #include "powerdomain.h" #include "clockdomain.h" #include "pm.h" #include "twl-common.h" -static struct omap_device_pm_latency *pm_lats; +#ifdef CONFIG_SUSPEND +/* + * omap_pm_suspend: points to a function that does the SoC-specific + * suspend work + */ +static int (*omap_pm_suspend)(void); +#endif + +#ifdef CONFIG_PM +/** + * struct omap2_oscillator - Describe the board main oscillator latencies + * @startup_time: oscillator startup latency + * @shutdown_time: oscillator shutdown latency + */ +struct omap2_oscillator { + u32 startup_time; + u32 shutdown_time; +}; + +static struct omap2_oscillator oscillator = { + .startup_time = ULONG_MAX, + .shutdown_time = ULONG_MAX, +}; -static int _init_omap_device(char *name) +void omap_pm_setup_oscillator(u32 tstart, u32 tshut) +{ + oscillator.startup_time = tstart; + oscillator.shutdown_time = tshut; +} + +void omap_pm_get_oscillator(u32 *tstart, u32 *tshut) +{ + if (!tstart || !tshut) + return; + + *tstart = oscillator.startup_time; + *tshut = oscillator.shutdown_time; +} +#endif + +static int __init _init_omap_device(char *name) { struct omap_hwmod *oh; struct platform_device *pdev; @@ -38,7 +82,7 @@ static int _init_omap_device(char *name) __func__, name)) return -ENODEV; - pdev = omap_device_build(oh->name, 0, oh, NULL, 0, pm_lats, 0, false); + pdev = omap_device_build(oh->name, 0, oh, NULL, 0); if (WARN(IS_ERR(pdev), "%s: could not build omap_device for %s\n", __func__, name)) return -ENODEV; @@ -49,7 +93,7 @@ static int _init_omap_device(char *name) /* * Build omap_devices for processors and bus. */ -static void omap2_init_processor_devices(void) +static void __init omap2_init_processor_devices(void) { _init_omap_device("mpu"); if (omap3_has_iva()) @@ -64,69 +108,16 @@ 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. - */ -int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state) +int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) { - u32 cur_state; - int sleep_switch = -1; - int ret = 0; - int hwsup = 0; - - 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) { - if ((pwrdm_read_pwrst(pwrdm) > state) && - (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) { - sleep_switch = LOWPOWERSTATE_SWITCH; - } else { - hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]); - clkdm_wakeup(pwrdm->pwrdm_clkdms[0]); - sleep_switch = FORCEWAKEUP_SWITCH; - } - } - - ret = pwrdm_set_next_pwrst(pwrdm, state); - if (ret) { - pr_err("%s: unable to set state of powerdomain: %s\n", - __func__, pwrdm->name); - goto err; - } - - switch (sleep_switch) { - case FORCEWAKEUP_SWITCH: - if (hwsup) - clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]); - else - clkdm_sleep(pwrdm->pwrdm_clkdms[0]); - break; - case LOWPOWERSTATE_SWITCH: - pwrdm_set_lowpwrstchange(pwrdm); - break; - default: - return ret; - } - - pwrdm_state_switch(pwrdm); -err: - return ret; + /* XXX The usecount test is racy */ + if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) && + !(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)) + clkdm_allow_idle(clkdm); + else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && + clkdm->usecount == 0) + clkdm_sleep(clkdm); + return 0; } /* @@ -142,7 +133,7 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, { struct voltagedomain *voltdm; struct clk *clk; - struct opp *opp; + struct dev_pm_opp *opp; unsigned long freq, bootup_volt; struct device *dev; @@ -151,7 +142,15 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, goto exit; } - dev = omap_device_get_by_hwmod_name(oh_name); + if (!strncmp(oh_name, "mpu", 3)) + /* + * All current OMAPs share voltage rail and clock + * source, so CPU0 is used to represent the MPU-SS. + */ + dev = get_cpu_device(0); + else + 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); @@ -159,7 +158,7 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, } voltdm = voltdm_lookup(vdd_name); - if (IS_ERR(voltdm)) { + if (!voltdm) { pr_err("%s: unable to get vdd pointer for vdd_%s\n", __func__, vdd_name); goto exit; @@ -171,20 +170,23 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name, goto exit; } - freq = clk->rate; + freq = clk_get_rate(clk); clk_put(clk); - opp = opp_find_freq_ceil(dev, &freq); + rcu_read_lock(); + opp = dev_pm_opp_find_freq_ceil(dev, &freq); if (IS_ERR(opp)) { + rcu_read_unlock(); pr_err("%s: unable to find boot up OPP for vdd_%s\n", __func__, vdd_name); goto exit; } - bootup_volt = opp_get_voltage(opp); + bootup_volt = dev_pm_opp_get_voltage(opp); + rcu_read_unlock(); if (!bootup_volt) { - pr_err("%s: unable to find voltage corresponding " - "to the bootup OPP for vdd_%s\n", __func__, vdd_name); + pr_err("%s: unable to find voltage corresponding to the bootup OPP for vdd_%s\n", + __func__, vdd_name); goto exit; } @@ -196,6 +198,64 @@ exit: return -EINVAL; } +#ifdef CONFIG_SUSPEND +static int omap_pm_enter(suspend_state_t suspend_state) +{ + int ret = 0; + + if (!omap_pm_suspend) + return -ENOENT; /* XXX doublecheck */ + + switch (suspend_state) { + case PM_SUSPEND_STANDBY: + case PM_SUSPEND_MEM: + ret = omap_pm_suspend(); + break; + default: + ret = -EINVAL; + } + + return ret; +} + +static int omap_pm_begin(suspend_state_t state) +{ + cpu_idle_poll_ctrl(true); + if (cpu_is_omap34xx()) + omap_prcm_irq_prepare(); + return 0; +} + +static void omap_pm_end(void) +{ + cpu_idle_poll_ctrl(false); +} + +static void omap_pm_finish(void) +{ + if (cpu_is_omap34xx()) + omap_prcm_irq_complete(); +} + +static const struct platform_suspend_ops omap_pm_ops = { + .begin = omap_pm_begin, + .end = omap_pm_end, + .enter = omap_pm_enter, + .finish = omap_pm_finish, + .valid = suspend_valid_only_mem, +}; + +/** + * omap_common_suspend_init - Set common suspend routines for OMAP SoCs + * @pm_suspend: function pointer to SoC specific suspend function + */ +void omap_common_suspend_init(void *pm_suspend) +{ + omap_pm_suspend = pm_suspend; + suspend_set_ops(&omap_pm_ops); +} +#endif /* CONFIG_SUSPEND */ + static void __init omap3_init_voltages(void) { if (!cpu_is_omap34xx()) @@ -215,6 +275,17 @@ static void __init omap4_init_voltages(void) omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", "iva"); } +static inline void omap_init_cpufreq(void) +{ + struct platform_device_info devinfo = { }; + + if (!of_have_populated_dt()) + devinfo.name = "omap-cpufreq"; + else + devinfo.name = "cpufreq-cpu0"; + platform_device_register_full(&devinfo); +} + static int __init omap2_common_pm_init(void) { if (!of_have_populated_dt()) @@ -223,10 +294,15 @@ static int __init omap2_common_pm_init(void) return 0; } -postcore_initcall(omap2_common_pm_init); +omap_postcore_initcall(omap2_common_pm_init); -static int __init omap2_common_pm_late_init(void) +int __init omap2_common_pm_late_init(void) { + if (of_have_populated_dt()) { + omap3_twl_init(); + omap4_twl_init(); + } + /* Init the voltage layer */ omap_pmic_late_init(); omap_voltage_late_init(); @@ -238,6 +314,8 @@ static int __init omap2_common_pm_late_init(void) /* Smartreflex device init */ omap_devinit_smartreflex(); + /* cpufreq dummy device instantiation */ + omap_init_cpufreq(); + return 0; } -late_initcall(omap2_common_pm_late_init); |
