766e7afc38
Since the device pointer is now retrieved using the hwmod name, remove the static variables used to store the device pointers for DSP, MPU, IVA and L3 devices for PM/DVFS usage. Signed-off-by: Benoit Cousson <b-cousson@ti.com> Signed-off-by: Kevin Hilman <khilman@ti.com>
244 lines
5.4 KiB
C
244 lines
5.4 KiB
C
/*
|
|
* 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>
|
|
#include <linux/opp.h>
|
|
|
|
#include <plat/omap-pm.h>
|
|
#include <plat/omap_device.h>
|
|
#include <plat/common.h>
|
|
|
|
#include "voltage.h"
|
|
#include "powerdomain.h"
|
|
#include "clockdomain.h"
|
|
#include "pm.h"
|
|
|
|
static struct omap_device_pm_latency *pm_lats;
|
|
|
|
static int _init_omap_device(char *name)
|
|
{
|
|
struct omap_hwmod *oh;
|
|
struct platform_device *pdev;
|
|
|
|
oh = omap_hwmod_lookup(name);
|
|
if (WARN(!oh, "%s: could not find omap_hwmod for %s\n",
|
|
__func__, name))
|
|
return -ENODEV;
|
|
|
|
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",
|
|
__func__, name))
|
|
return -ENODEV;
|
|
|
|
return 0;
|
|
}
|
|
|
|
/*
|
|
* Build omap_devices for processors and bus.
|
|
*/
|
|
static void omap2_init_processor_devices(void)
|
|
{
|
|
_init_omap_device("mpu");
|
|
if (omap3_has_iva())
|
|
_init_omap_device("iva");
|
|
|
|
if (cpu_is_omap44xx()) {
|
|
_init_omap_device("l3_main_1");
|
|
_init_omap_device("dsp");
|
|
_init_omap_device("iva");
|
|
} else {
|
|
_init_omap_device("l3_main");
|
|
}
|
|
}
|
|
|
|
/* 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)
|
|
{
|
|
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;
|
|
}
|
|
|
|
/*
|
|
* This API is to be called during init to set 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 sets the voltage domain to the voltage specified
|
|
* in the opp entry
|
|
*/
|
|
static int __init omap2_set_init_voltage(char *vdd_name, char *clk_name,
|
|
const char *oh_name)
|
|
{
|
|
struct voltagedomain *voltdm;
|
|
struct clk *clk;
|
|
struct opp *opp;
|
|
unsigned long freq, bootup_volt;
|
|
struct device *dev;
|
|
|
|
if (!vdd_name || !clk_name || !oh_name) {
|
|
pr_err("%s: invalid parameters\n", __func__);
|
|
goto exit;
|
|
}
|
|
|
|
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;
|
|
}
|
|
|
|
voltdm = voltdm_lookup(vdd_name);
|
|
if (IS_ERR(voltdm)) {
|
|
pr_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)) {
|
|
pr_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)) {
|
|
pr_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) {
|
|
pr_err("%s: unable to find voltage corresponding "
|
|
"to the bootup OPP for vdd_%s\n", __func__, vdd_name);
|
|
goto exit;
|
|
}
|
|
|
|
voltdm_scale(voltdm, bootup_volt);
|
|
return 0;
|
|
|
|
exit:
|
|
pr_err("%s: unable to set vdd_%s\n", __func__, vdd_name);
|
|
return -EINVAL;
|
|
}
|
|
|
|
static void __init omap3_init_voltages(void)
|
|
{
|
|
if (!cpu_is_omap34xx())
|
|
return;
|
|
|
|
omap2_set_init_voltage("mpu_iva", "dpll1_ck", "mpu");
|
|
omap2_set_init_voltage("core", "l3_ick", "l3_main");
|
|
}
|
|
|
|
static void __init omap4_init_voltages(void)
|
|
{
|
|
if (!cpu_is_omap44xx())
|
|
return;
|
|
|
|
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");
|
|
}
|
|
|
|
static int __init omap2_common_pm_init(void)
|
|
{
|
|
omap2_init_processor_devices();
|
|
omap_pm_if_init();
|
|
|
|
return 0;
|
|
}
|
|
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);
|