aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2/pm.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/pm.c')
-rw-r--r--arch/arm/mach-omap2/pm.c272
1 files changed, 253 insertions, 19 deletions
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index 49486f5..4464039 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -24,30 +24,82 @@
#include "clockdomain.h"
#include "pm.h"
+/**
+ * struct omap2_pm_lp_description - Describe low power behavior of the system
+ * @oscillator_startup_time: Time rounded up to uSec for the oscillator to
+ * provide a stable clock from power on.
+ * @oscillator_shutdown_time: Time rounded up to uSec for oscillator to safely
+ * switch off.
+ * @pmic_startup_time: Time rounded up to uSec for the PMIC to
+ * provide be ready for operation from low power
+ * state. Note: this is not the same as voltage
+ * rampup time, instead, consider the PMIC to be
+ * in lowest power state(say OFF), this is the time
+ * required for it to become ready for it's DCDCs
+ * or LDOs to start operation.
+ * @pmic_shutdown_time: Time rounded up to uSec for the PMIC to
+ * go to low power after the LDOs are pulled to
+ * appropriate state. Note: this is not the same as
+ * voltage rampdown time, instead, consider the
+ * PMIC to have switched it's LDOs down, this is
+ * time taken to reach it's lowest power state(say
+ * sleep/OFF).
+ *
+ * With complex systems like OMAP, we need a generic description of system
+ * behavior beyond the normal description of device/peripheral operation
+ * which in conjunction with other parameters describe and control the low
+ * power operation of the device. This information tends to be specific
+ * to every board.
+ */
+struct omap2_pm_lp_description {
+ u32 oscillator_startup_time;
+ u32 oscillator_shutdown_time;
+ u32 pmic_startup_time;
+ u32 pmic_shutdown_time;
+};
+
+/*
+ * Setup time to be the max... we want to err towards the worst
+ * as default. rest of the system can populate these with more
+ * optimal values
+ */
+static struct omap2_pm_lp_description _pm_lp_desc = {
+ .oscillator_startup_time = ULONG_MAX,
+ .oscillator_shutdown_time = ULONG_MAX,
+ .pmic_startup_time = ULONG_MAX,
+ .pmic_shutdown_time = ULONG_MAX,
+};
+
static struct omap_device_pm_latency *pm_lats;
static struct device *mpu_dev;
static struct device *iva_dev;
static struct device *l3_dev;
static struct device *dsp_dev;
+static struct device *fdif_dev;
+
+bool omap_pm_is_ready_status;
struct device *omap2_get_mpuss_device(void)
{
WARN_ON_ONCE(!mpu_dev);
return mpu_dev;
}
+EXPORT_SYMBOL(omap2_get_mpuss_device);
struct device *omap2_get_iva_device(void)
{
WARN_ON_ONCE(!iva_dev);
return iva_dev;
}
+EXPORT_SYMBOL(omap2_get_iva_device);
struct device *omap2_get_l3_device(void)
{
WARN_ON_ONCE(!l3_dev);
return l3_dev;
}
+EXPORT_SYMBOL(omap2_get_l3_device);
struct device *omap4_get_dsp_device(void)
{
@@ -56,6 +108,89 @@ struct device *omap4_get_dsp_device(void)
}
EXPORT_SYMBOL(omap4_get_dsp_device);
+struct device *omap4_get_fdif_device(void)
+{
+ WARN_ON_ONCE(!fdif_dev);
+ return fdif_dev;
+}
+EXPORT_SYMBOL(omap4_get_fdif_device);
+
+/**
+ * omap_pm_get_pmic_lp_time() - retrieve the oscillator time
+ * @tstart: pointer to startup time in uSec
+ * @tshut: pointer to shutdown time in uSec
+ *
+ * if the pointers are invalid, returns error, else
+ * populates the tstart and tshut values with the currently
+ * stored values.
+ */
+int omap_pm_get_osc_lp_time(u32 *tstart, u32 *tshut)
+{
+ if (!tstart || !tshut)
+ return -EINVAL;
+
+ *tstart = _pm_lp_desc.oscillator_startup_time;
+ *tshut = _pm_lp_desc.oscillator_shutdown_time;
+
+ return 0;
+}
+
+/**
+ * omap_pm_get_pmic_lp_time() - retrieve the PMIC time
+ * @tstart: pointer to startup time in uSec
+ * @tshut: pointer to shutdown time in uSec
+ *
+ * if the pointers are invalid, returns error, else
+ * populates the tstart and tshut values with the currently
+ * stored values.
+ */
+int omap_pm_get_pmic_lp_time(u32 *tstart, u32 *tshut)
+{
+ if (!tstart || !tshut)
+ return -EINVAL;
+
+ *tstart = _pm_lp_desc.pmic_startup_time;
+ *tshut = _pm_lp_desc.pmic_shutdown_time;
+
+ return 0;
+}
+
+/**
+ * omap_pm_set_osc_lp_time() - setup the system oscillator time
+ * @tstart: startup time rounded up to uSec
+ * @tshut: shutdown time rounded up to uSec
+ *
+ * All boards do need an oscillator for the device to function.
+ * The startup and stop time of these oscillators vary. Populate
+ * from the board file to optimize the timing.
+ * This function is meant to be used at boot-time configuration.
+ *
+ * NOTE: This API is intended to be invoked from board file
+ */
+void __init omap_pm_set_osc_lp_time(u32 tstart, u32 tshut)
+{
+ _pm_lp_desc.oscillator_startup_time = tstart;
+ _pm_lp_desc.oscillator_shutdown_time = tshut;
+}
+
+/**
+ * omap_pm_set_pmic_lp_time() - setup the pmic low power time
+ * @tstart: startup time rounded up to uSec
+ * @tshut: shutdown time rounded up to uSec
+ *
+ * Store the time for PMIC to enter to lowest state supported.
+ * in the case of multiple PMIC on a platform, choose the one
+ * that ends the sequence for LP state such as OFF and starts
+ * the sequence such as wakeup from OFF - e.g. a PMIC that
+ * controls core-domain.
+ * This function is meant to be used at boot-time configuration.
+ */
+void __init omap_pm_set_pmic_lp_time(u32 tstart, u32 tshut)
+{
+ _pm_lp_desc.pmic_startup_time = tstart;
+ _pm_lp_desc.pmic_shutdown_time = tshut;
+}
+
/* static int _init_omap_device(struct omap_hwmod *oh, void *user) */
static int _init_omap_device(char *name, struct device **new_dev)
{
@@ -90,6 +225,7 @@ static void omap2_init_processor_devices(void)
_init_omap_device("l3_main_1", &l3_dev);
_init_omap_device("dsp", &dsp_dev);
_init_omap_device("iva", &iva_dev);
+ _init_omap_device("fdif", &fdif_dev);
} else {
_init_omap_device("l3_main", &l3_dev);
}
@@ -106,8 +242,9 @@ static void omap2_init_processor_devices(void)
int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
{
u32 cur_state;
- int sleep_switch = 0;
+ int sleep_switch = -1;
int ret = 0;
+ int hwsup = 0;
if (pwrdm == NULL || IS_ERR(pwrdm))
return -EINVAL;
@@ -127,6 +264,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
sleep_switch = LOWPOWERSTATE_SWITCH;
} else {
+ hwsup = clkdm_is_idle(pwrdm->pwrdm_clkdms[0]);
clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
pwrdm_wait_transition(pwrdm);
sleep_switch = FORCEWAKEUP_SWITCH;
@@ -142,7 +280,7 @@ int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state)
switch (sleep_switch) {
case FORCEWAKEUP_SWITCH:
- if (pwrdm->pwrdm_clkdms[0]->flags & CLKDM_CAN_ENABLE_AUTO)
+ if (hwsup)
clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
else
clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
@@ -160,6 +298,45 @@ err:
return ret;
}
+static int __init boot_volt_scale(struct voltagedomain *voltdm,
+ unsigned long boot_v)
+{
+ struct omap_volt_data *vdata;
+ int ret = 0;
+
+ vdata = omap_voltage_get_voltdata(voltdm, boot_v);
+ if (IS_ERR_OR_NULL(vdata)) {
+ pr_err("%s:%s: Bad New voltage data for %ld\n",
+ __func__, voltdm->name, boot_v);
+ return PTR_ERR(vdata);
+ }
+ /*
+ * DO NOT DO abb prescale -
+ * case 1: OPP needs FBB, bootloader configured FBB
+ * - doing a prescale results in bypass -> system fail
+ * case 2: OPP needs FBB, bootloader does not configure FBB
+ * - FBB will be configured in postscale
+ * case 3: OPP needs bypass, bootloader configures FBB
+ * - bypass will be configured in postscale
+ * case 4: OPP needs bypass, bootloader configured in bypass
+ * - bypass programming in postscale skipped
+ */
+ ret = voltdm_scale(voltdm, vdata);
+ if (ret) {
+ pr_err("%s: Fail set voltage(v=%ld)on vdd%s\n",
+ __func__, boot_v, voltdm->name);
+ return ret;
+ }
+ if (voltdm->abb) {
+ ret = omap_ldo_abb_post_scale(voltdm, vdata);
+ if (ret) {
+ pr_err("%s: Fail abb postscale(v=%ld)vdd%s\n",
+ __func__, boot_v, voltdm->name);
+ }
+ }
+ 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
@@ -174,14 +351,15 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
struct voltagedomain *voltdm;
struct clk *clk;
struct opp *opp;
- unsigned long freq, bootup_volt;
+ unsigned long freq_cur, freq_valid, bootup_volt;
+ int ret = -EINVAL;
if (!vdd_name || !clk_name || !dev) {
printk(KERN_ERR "%s: Invalid parameters!\n", __func__);
goto exit;
}
- voltdm = omap_voltage_domain_lookup(vdd_name);
+ voltdm = voltdm_lookup(vdd_name);
if (IS_ERR(voltdm)) {
printk(KERN_ERR "%s: Unable to get vdd pointer for vdd_%s\n",
__func__, vdd_name);
@@ -195,25 +373,78 @@ static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
goto exit;
}
- freq = clk->rate;
- clk_put(clk);
+ freq_cur = clk->rate;
+ freq_valid = freq_cur;
- opp = opp_find_freq_ceil(dev, &freq);
+ rcu_read_lock();
+ opp = opp_find_freq_ceil(dev, &freq_valid);
if (IS_ERR(opp)) {
- printk(KERN_ERR "%s: unable to find boot up OPP for vdd_%s\n",
- __func__, vdd_name);
- goto exit;
+ opp = opp_find_freq_floor(dev, &freq_valid);
+ if (IS_ERR(opp)) {
+ rcu_read_unlock();
+ pr_err("%s: no boot OPP match for %ld on vdd_%s\n",
+ __func__, freq_cur, vdd_name);
+ ret = -ENOENT;
+ goto exit_ck;
+ }
}
bootup_volt = opp_get_voltage(opp);
+ rcu_read_unlock();
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;
+ ret = -ENOENT;
+ goto exit_ck;
}
- omap_voltage_scale_vdd(voltdm, bootup_volt);
- return 0;
+ /*
+ * Frequency and Voltage have to be sequenced: if we move from
+ * a lower frequency to higher frequency, raise voltage, followed by
+ * frequency, and vice versa. we assume that the voltage at boot
+ * is the required voltage for the frequency it was set for.
+ * NOTE:
+ * we can check the frequency, but there is numerous ways to set
+ * voltage. We play the safe path and just set the voltage.
+ */
+
+ if (freq_cur < freq_valid) {
+ ret = boot_volt_scale(voltdm, bootup_volt);
+ if (ret) {
+ pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n",
+ __func__, vdd_name, freq_valid,
+ bootup_volt, vdd_name);
+ goto exit_ck;
+ }
+ }
+
+ /* Set freq only if there is a difference in freq */
+ if (freq_valid != freq_cur) {
+ ret = clk_set_rate(clk, freq_valid);
+ if (ret) {
+ pr_err("%s: Fail set clk-%s(f=%ld v=%ld)on vdd%s\n",
+ __func__, clk_name, freq_valid,
+ bootup_volt, vdd_name);
+ goto exit_ck;
+ }
+ }
+
+ if (freq_cur >= freq_valid) {
+ ret = boot_volt_scale(voltdm, bootup_volt);
+ if (ret) {
+ pr_err("%s: Fail set voltage-%s(f=%ld v=%ld)on vdd%s\n",
+ __func__, clk_name, freq_valid,
+ bootup_volt, vdd_name);
+ goto exit_ck;
+ }
+ }
+
+ ret = 0;
+exit_ck:
+ clk_put(clk);
+
+ if (!ret)
+ return 0;
exit:
printk(KERN_ERR "%s: Unable to put vdd_%s to its init voltage\n\n",
@@ -226,7 +457,7 @@ 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("mpu_iva", "dpll1_ck", mpu_dev);
omap2_set_init_voltage("core", "l3_ick", l3_dev);
}
@@ -235,8 +466,12 @@ 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);
+ if (cpu_is_omap446x()) {
+ omap2_set_init_voltage("mpu", "virt_dpll_mpu_ck", mpu_dev);
+ } else {
+ omap2_set_init_voltage("mpu", "dpll_mpu_ck", mpu_dev);
+ }
+ omap2_set_init_voltage("core", "virt_l3_ck", l3_dev);
omap2_set_init_voltage("iva", "dpll_iva_m5x2_ck", iva_dev);
}
@@ -251,9 +486,8 @@ 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 OMAP PMIC parameters */
+ omap_pmic_data_init();
/* Init the voltage layer */
omap_voltage_late_init();