Skip to content

Commit

Permalink
OMAP2/3 clkdm/pwrdm: move wkdep/sleepdep handling from pwrdm to clkdm
Browse files Browse the repository at this point in the history
Move clockdomain wakeup dependency and sleep dependency data
structures from the powerdomain layer to the clockdomain layer, where
they belong.  These dependencies were originally placed in the
powerdomain layer due to unclear documentation; however, it is clear
now that these dependencies are between clockdomains.  For OMAP2/3,
this is not such a big problem, but for OMAP4 this needs to be fixed.

Thanks to Benoît Cousson <b-cousson@ti.com> for his advice on this
patch.

Signed-off-by: Paul Walmsley <paul@pwsan.com>
Cc: Benoît Cousson <b-cousson@ti.com>
  • Loading branch information
Paul Walmsley committed Jan 27, 2010
1 parent 6b04e0d commit 55ed969
Show file tree
Hide file tree
Showing 13 changed files with 761 additions and 690 deletions.
368 changes: 307 additions & 61 deletions arch/arm/mach-omap2/clockdomain.c

Large diffs are not rendered by default.

378 changes: 367 additions & 11 deletions arch/arm/mach-omap2/clockdomains.h

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion arch/arm/mach-omap2/io.c
Original file line number Diff line number Diff line change
Expand Up @@ -313,7 +313,7 @@ void __init omap2_init_common_hw(struct omap_sdrc_params *sdrc_cs0,
hwmods = omap34xx_hwmods;

pwrdm_init(powerdomains_omap);
clkdm_init(clockdomains_omap, clkdm_pwrdm_autodeps);
clkdm_init(clockdomains_omap, clkdm_autodeps);
#ifndef CONFIG_ARCH_OMAP4 /* FIXME: Remove this once the clkdev is ready */
/* The OPP tables have to be registered before a clk init */
omap_hwmod_init(hwmods);
Expand Down
10 changes: 4 additions & 6 deletions arch/arm/mach-omap2/omap_hwmod.c
Original file line number Diff line number Diff line change
Expand Up @@ -299,15 +299,14 @@ static int _disable_wakeup(struct omap_hwmod *oh)
* be accessed by the IVA, there should be a sleepdep between the IVA
* initiator and the module). Only applies to modules in smart-idle
* mode. Returns -EINVAL upon error or passes along
* pwrdm_add_sleepdep() value upon success.
* clkdm_add_sleepdep() value upon success.
*/
static int _add_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)
{
if (!oh->_clk)
return -EINVAL;

return pwrdm_add_sleepdep(oh->_clk->clkdm->pwrdm.ptr,
init_oh->_clk->clkdm->pwrdm.ptr);
return clkdm_add_sleepdep(oh->_clk->clkdm, init_oh->_clk->clkdm);
}

/**
Expand All @@ -320,15 +319,14 @@ static int _add_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)
* be accessed by the IVA, there should be no sleepdep between the IVA
* initiator and the module). Only applies to modules in smart-idle
* mode. Returns -EINVAL upon error or passes along
* pwrdm_add_sleepdep() value upon success.
* clkdm_del_sleepdep() value upon success.
*/
static int _del_initiator_dep(struct omap_hwmod *oh, struct omap_hwmod *init_oh)
{
if (!oh->_clk)
return -EINVAL;

return pwrdm_del_sleepdep(oh->_clk->clkdm->pwrdm.ptr,
init_oh->_clk->clkdm->pwrdm.ptr);
return clkdm_del_sleepdep(oh->_clk->clkdm, init_oh->_clk->clkdm);
}

/**
Expand Down
10 changes: 8 additions & 2 deletions arch/arm/mach-omap2/pm34xx.c
Original file line number Diff line number Diff line change
Expand Up @@ -1018,6 +1018,7 @@ void omap_push_sram_idle(void)
static int __init omap3_pm_init(void)
{
struct power_state *pwrst, *tmp;
struct clockdomain *neon_clkdm, *per_clkdm, *mpu_clkdm, *core_clkdm;
int ret;

if (!cpu_is_omap34xx())
Expand Down Expand Up @@ -1057,6 +1058,11 @@ static int __init omap3_pm_init(void)
core_pwrdm = pwrdm_lookup("core_pwrdm");
cam_pwrdm = pwrdm_lookup("cam_pwrdm");

neon_clkdm = clkdm_lookup("neon_clkdm");
mpu_clkdm = clkdm_lookup("mpu_clkdm");
per_clkdm = clkdm_lookup("per_clkdm");
core_clkdm = clkdm_lookup("core_clkdm");

omap_push_sram_idle();
#ifdef CONFIG_SUSPEND
suspend_set_ops(&omap_pm_ops);
Expand All @@ -1065,14 +1071,14 @@ static int __init omap3_pm_init(void)
pm_idle = omap3_pm_idle;
omap3_idle_init();

pwrdm_add_wkdep(neon_pwrdm, mpu_pwrdm);
clkdm_add_wkdep(neon_clkdm, mpu_clkdm);
/*
* REVISIT: This wkdep is only necessary when GPIO2-6 are enabled for
* IO-pad wakeup. Otherwise it will unnecessarily waste power
* waking up PER with every CORE wakeup - see
* http://marc.info/?l=linux-omap&m=121852150710062&w=2
*/
pwrdm_add_wkdep(per_pwrdm, core_pwrdm);
clkdm_add_wkdep(per_clkdm, core_clkdm);

if (omap_type() != OMAP2_DEVICE_TYPE_GP) {
omap3_secure_ram_storage =
Expand Down
259 changes: 2 additions & 257 deletions arch/arm/mach-omap2/powerdomain.c
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
* OMAP powerdomain control
*
* Copyright (C) 2007-2008 Texas Instruments, Inc.
* Copyright (C) 2007-2008 Nokia Corporation
* Copyright (C) 2007-2009 Nokia Corporation
*
* Written by Paul Walmsley
*
Expand Down Expand Up @@ -36,6 +36,7 @@
#include <plat/cpu.h>
#include <plat/powerdomain.h>
#include <plat/clockdomain.h>
#include <plat/prcm.h>

#include "pm.h"

Expand Down Expand Up @@ -88,17 +89,6 @@ static DEFINE_RWLOCK(pwrdm_rwlock);

/* Private functions */

static u32 prm_read_mod_bits_shift(s16 domain, s16 idx, u32 mask)
{
u32 v;

v = prm_read_mod_reg(domain, idx);
v &= mask;
v >>= __ffs(mask);

return v;
}

static struct powerdomain *_pwrdm_lookup(const char *name)
{
struct powerdomain *pwrdm, *temp_pwrdm;
Expand All @@ -115,34 +105,6 @@ static struct powerdomain *_pwrdm_lookup(const char *name)
return pwrdm;
}

/* _pwrdm_deps_lookup - look up the specified powerdomain in a pwrdm list */
static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm,
struct pwrdm_dep *deps)
{
struct pwrdm_dep *pd;

if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip))
return ERR_PTR(-EINVAL);

for (pd = deps; pd->pwrdm_name; pd++) {

if (!omap_chip_is(pd->omap_chip))
continue;

if (!pd->pwrdm && pd->pwrdm_name)
pd->pwrdm = pwrdm_lookup(pd->pwrdm_name);

if (pd->pwrdm == pwrdm)
break;

}

if (!pd->pwrdm_name)
return ERR_PTR(-ENOENT);

return pd->pwrdm;
}

static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
{

Expand Down Expand Up @@ -502,223 +464,6 @@ int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
return ret;
}


/**
* pwrdm_add_wkdep - add a wakeup dependency from pwrdm2 to pwrdm1
* @pwrdm1: wake this struct powerdomain * up (dependent)
* @pwrdm2: when this struct powerdomain * wakes up (source)
*
* When the powerdomain represented by pwrdm2 wakes up (due to an
* interrupt), wake up pwrdm1. Implemented in hardware on the OMAP,
* this feature is designed to reduce wakeup latency of the dependent
* powerdomain. Returns -EINVAL if presented with invalid powerdomain
* pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or
* 0 upon success.
*/
int pwrdm_add_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear wake up of "
"%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
return PTR_ERR(p);
}

pr_debug("powerdomain: hardware will wake up %s when %s wakes up\n",
pwrdm1->name, pwrdm2->name);

prm_set_mod_reg_bits((1 << pwrdm2->dep_bit),
pwrdm1->prcm_offs, PM_WKDEP);

return 0;
}

/**
* pwrdm_del_wkdep - remove a wakeup dependency from pwrdm2 to pwrdm1
* @pwrdm1: wake this struct powerdomain * up (dependent)
* @pwrdm2: when this struct powerdomain * wakes up (source)
*
* Remove a wakeup dependency that causes pwrdm1 to wake up when pwrdm2
* wakes up. Returns -EINVAL if presented with invalid powerdomain
* pointers, -ENOENT if pwrdm2 cannot wake up pwrdm1 in hardware, or
* 0 upon success.
*/
int pwrdm_del_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear wake up of "
"%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
return PTR_ERR(p);
}

pr_debug("powerdomain: hardware will no longer wake up %s after %s "
"wakes up\n", pwrdm1->name, pwrdm2->name);

prm_clear_mod_reg_bits((1 << pwrdm2->dep_bit),
pwrdm1->prcm_offs, PM_WKDEP);

return 0;
}

/**
* pwrdm_read_wkdep - read wakeup dependency state from pwrdm2 to pwrdm1
* @pwrdm1: wake this struct powerdomain * up (dependent)
* @pwrdm2: when this struct powerdomain * wakes up (source)
*
* Return 1 if a hardware wakeup dependency exists wherein pwrdm1 will be
* awoken when pwrdm2 wakes up; 0 if dependency is not set; -EINVAL
* if either powerdomain pointer is invalid; or -ENOENT if the hardware
* is incapable.
*
* REVISIT: Currently this function only represents software-controllable
* wakeup dependencies. Wakeup dependencies fixed in hardware are not
* yet handled here.
*/
int pwrdm_read_wkdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->wkdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear wake up of "
"%s when %s wakes up\n", pwrdm1->name, pwrdm2->name);
return PTR_ERR(p);
}

return prm_read_mod_bits_shift(pwrdm1->prcm_offs, PM_WKDEP,
(1 << pwrdm2->dep_bit));
}

/**
* pwrdm_add_sleepdep - add a sleep dependency from pwrdm2 to pwrdm1
* @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
* @pwrdm2: when this struct powerdomain * is active (source)
*
* Prevent pwrdm1 from automatically going inactive (and then to
* retention or off) if pwrdm2 is still active. Returns -EINVAL if
* presented with invalid powerdomain pointers or called on a machine
* that does not support software-configurable hardware sleep dependencies,
* -ENOENT if the specified dependency cannot be set in hardware, or
* 0 upon success.
*/
int pwrdm_add_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!cpu_is_omap34xx())
return -EINVAL;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear sleep "
"dependency affecting %s from %s\n", pwrdm1->name,
pwrdm2->name);
return PTR_ERR(p);
}

pr_debug("powerdomain: will prevent %s from sleeping if %s is active\n",
pwrdm1->name, pwrdm2->name);

cm_set_mod_reg_bits((1 << pwrdm2->dep_bit),
pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP);

return 0;
}

/**
* pwrdm_del_sleepdep - remove a sleep dependency from pwrdm2 to pwrdm1
* @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
* @pwrdm2: when this struct powerdomain * is active (source)
*
* Allow pwrdm1 to automatically go inactive (and then to retention or
* off), independent of the activity state of pwrdm2. Returns -EINVAL
* if presented with invalid powerdomain pointers or called on a machine
* that does not support software-configurable hardware sleep dependencies,
* -ENOENT if the specified dependency cannot be cleared in hardware, or
* 0 upon success.
*/
int pwrdm_del_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!cpu_is_omap34xx())
return -EINVAL;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear sleep "
"dependency affecting %s from %s\n", pwrdm1->name,
pwrdm2->name);
return PTR_ERR(p);
}

pr_debug("powerdomain: will no longer prevent %s from sleeping if "
"%s is active\n", pwrdm1->name, pwrdm2->name);

cm_clear_mod_reg_bits((1 << pwrdm2->dep_bit),
pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP);

return 0;
}

/**
* pwrdm_read_sleepdep - read sleep dependency state from pwrdm2 to pwrdm1
* @pwrdm1: prevent this struct powerdomain * from sleeping (dependent)
* @pwrdm2: when this struct powerdomain * is active (source)
*
* Return 1 if a hardware sleep dependency exists wherein pwrdm1 will
* not be allowed to automatically go inactive if pwrdm2 is active;
* 0 if pwrdm1's automatic power state inactivity transition is independent
* of pwrdm2's; -EINVAL if either powerdomain pointer is invalid or called
* on a machine that does not support software-configurable hardware sleep
* dependencies; or -ENOENT if the hardware is incapable.
*
* REVISIT: Currently this function only represents software-controllable
* sleep dependencies. Sleep dependencies fixed in hardware are not
* yet handled here.
*/
int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2)
{
struct powerdomain *p;

if (!cpu_is_omap34xx())
return -EINVAL;

if (!pwrdm1)
return -EINVAL;

p = _pwrdm_deps_lookup(pwrdm2, pwrdm1->sleepdep_srcs);
if (IS_ERR(p)) {
pr_debug("powerdomain: hardware cannot set/clear sleep "
"dependency affecting %s from %s\n", pwrdm1->name,
pwrdm2->name);
return PTR_ERR(p);
}

return prm_read_mod_bits_shift(pwrdm1->prcm_offs, OMAP3430_CM_SLEEPDEP,
(1 << pwrdm2->dep_bit));
}

/**
* pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
* @pwrdm: struct powerdomain *
Expand Down
Loading

0 comments on commit 55ed969

Please sign in to comment.