Commit 0e084c9c authored by Tony Lindgren's avatar Tony Lindgren

Merge tag 'omap-cleanup-b-for-3.9' of...

Merge tag 'omap-cleanup-b-for-3.9' of git://git.kernel.org/pub/scm/linux/kernel/git/pjw/omap-pending into omap-for-v3.9/pm

Several OMAP2+ power management fixes, optimizations, and cleanup.
This series is a prerequisite for the functional powerdomain
conversion series.

Basic test logs for this branch are here:

    http://www.pwsan.com/omap/testlogs/pm_cleanup_fixes_3.9/20130129150017/
parents 7b4bc079 562e54d1
...@@ -92,8 +92,6 @@ static int _clkdm_register(struct clockdomain *clkdm) ...@@ -92,8 +92,6 @@ static int _clkdm_register(struct clockdomain *clkdm)
pwrdm_add_clkdm(pwrdm, clkdm); pwrdm_add_clkdm(pwrdm, clkdm);
spin_lock_init(&clkdm->lock);
pr_debug("clockdomain: registered %s\n", clkdm->name); pr_debug("clockdomain: registered %s\n", clkdm->name);
return 0; return 0;
...@@ -122,7 +120,7 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm, ...@@ -122,7 +120,7 @@ static struct clkdm_dep *_clkdm_deps_lookup(struct clockdomain *clkdm,
return cd; return cd;
} }
/* /**
* _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store * _autodep_lookup - resolve autodep clkdm names to clkdm pointers; store
* @autodep: struct clkdm_autodep * to resolve * @autodep: struct clkdm_autodep * to resolve
* *
...@@ -154,88 +152,206 @@ static void _autodep_lookup(struct clkdm_autodep *autodep) ...@@ -154,88 +152,206 @@ static void _autodep_lookup(struct clkdm_autodep *autodep)
autodep->clkdm.ptr = clkdm; autodep->clkdm.ptr = clkdm;
} }
/* /**
* _clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable * _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms
* @clkdm: struct clockdomain * * @clkdm: clockdomain that we are resolving dependencies for
* @clkdm_deps: ptr to array of struct clkdm_deps to resolve
* *
* Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm' * Iterates through @clkdm_deps, looking up the struct clockdomain named by
* in hardware-supervised mode. Meant to be called from clock framework * clkdm_name and storing the clockdomain pointer in the struct clkdm_dep.
* when a clock inside clockdomain 'clkdm' is enabled. No return value. * No return value.
*/
static void _resolve_clkdm_deps(struct clockdomain *clkdm,
struct clkdm_dep *clkdm_deps)
{
struct clkdm_dep *cd;
for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) {
if (cd->clkdm)
continue;
cd->clkdm = _clkdm_lookup(cd->clkdm_name);
WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen",
clkdm->name, cd->clkdm_name);
}
}
/**
* _clkdm_add_wkdep - add a wakeup dependency from clkdm2 to clkdm1 (lockless)
* @clkdm1: wake this struct clockdomain * up (dependent)
* @clkdm2: when this struct clockdomain * wakes up (source)
* *
* XXX autodeps are deprecated and should be removed at the earliest * When the clockdomain represented by @clkdm2 wakes up, wake up
* opportunity * @clkdm1. Implemented in hardware on the OMAP, this feature is
* designed to reduce wakeup latency of the dependent clockdomain @clkdm1.
* Returns -EINVAL if presented with invalid clockdomain pointers,
* -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or 0 upon
* success.
*/ */
void _clkdm_add_autodeps(struct clockdomain *clkdm) static int _clkdm_add_wkdep(struct clockdomain *clkdm1,
struct clockdomain *clkdm2)
{ {
struct clkdm_autodep *autodep; struct clkdm_dep *cd;
int ret = 0;
if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) if (!clkdm1 || !clkdm2)
return; return -EINVAL;
for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(autodep->clkdm.ptr)) if (IS_ERR(cd))
continue; ret = PTR_ERR(cd);
pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n", if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep)
clkdm->name, autodep->clkdm.ptr->name); ret = -EINVAL;
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
return ret;
}
cd->wkdep_usecount++;
if (cd->wkdep_usecount == 1) {
pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr); ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
} }
return ret;
} }
/* /**
* _clkdm_add_autodeps - remove auto sleepdeps/wkdeps from clkdm * _clkdm_del_wkdep - remove a wakeup dep from clkdm2 to clkdm1 (lockless)
* @clkdm: struct clockdomain * * @clkdm1: wake this struct clockdomain * up (dependent)
* @clkdm2: when this struct clockdomain * wakes up (source)
* *
* Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm' * Remove a wakeup dependency causing @clkdm1 to wake up when @clkdm2
* in hardware-supervised mode. Meant to be called from clock framework * wakes up. Returns -EINVAL if presented with invalid clockdomain
* when a clock inside clockdomain 'clkdm' is disabled. No return value. * pointers, -ENOENT if @clkdm2 cannot wake up clkdm1 in hardware, or
* 0 upon success.
*/
static int _clkdm_del_wkdep(struct clockdomain *clkdm1,
struct clockdomain *clkdm2)
{
struct clkdm_dep *cd;
int ret = 0;
if (!clkdm1 || !clkdm2)
return -EINVAL;
cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(cd))
ret = PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep)
ret = -EINVAL;
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
return ret;
}
cd->wkdep_usecount--;
if (cd->wkdep_usecount == 0) {
pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
}
return ret;
}
/**
* _clkdm_add_sleepdep - add a sleep dependency from clkdm2 to clkdm1 (lockless)
* @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
* @clkdm2: when this struct clockdomain * is active (source)
* *
* XXX autodeps are deprecated and should be removed at the earliest * Prevent @clkdm1 from automatically going inactive (and then to
* opportunity * retention or off) if @clkdm2 is active. Returns -EINVAL if
* presented with invalid clockdomain 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.
*/ */
void _clkdm_del_autodeps(struct clockdomain *clkdm) static int _clkdm_add_sleepdep(struct clockdomain *clkdm1,
struct clockdomain *clkdm2)
{ {
struct clkdm_autodep *autodep; struct clkdm_dep *cd;
int ret = 0;
if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS) if (!clkdm1 || !clkdm2)
return; return -EINVAL;
for (autodep = autodeps; autodep->clkdm.ptr; autodep++) { cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
if (IS_ERR(autodep->clkdm.ptr)) if (IS_ERR(cd))
continue; ret = PTR_ERR(cd);
pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n", if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep)
clkdm->name, autodep->clkdm.ptr->name); ret = -EINVAL;
clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr); if (ret) {
clkdm_del_wkdep(clkdm, autodep->clkdm.ptr); pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
clkdm1->name, clkdm2->name);
return ret;
}
cd->sleepdep_usecount++;
if (cd->sleepdep_usecount == 1) {
pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
} }
return ret;
} }
/** /**
* _resolve_clkdm_deps() - resolve clkdm_names in @clkdm_deps to clkdms * _clkdm_del_sleepdep - remove a sleep dep from clkdm2 to clkdm1 (lockless)
* @clkdm: clockdomain that we are resolving dependencies for * @clkdm1: prevent this struct clockdomain * from sleeping (dependent)
* @clkdm_deps: ptr to array of struct clkdm_deps to resolve * @clkdm2: when this struct clockdomain * is active (source)
* *
* Iterates through @clkdm_deps, looking up the struct clockdomain named by * Allow @clkdm1 to automatically go inactive (and then to retention or
* clkdm_name and storing the clockdomain pointer in the struct clkdm_dep. * off), independent of the activity state of @clkdm2. Returns -EINVAL
* No return value. * if presented with invalid clockdomain 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.
*/ */
static void _resolve_clkdm_deps(struct clockdomain *clkdm, static int _clkdm_del_sleepdep(struct clockdomain *clkdm1,
struct clkdm_dep *clkdm_deps) struct clockdomain *clkdm2)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
int ret = 0;
for (cd = clkdm_deps; cd && cd->clkdm_name; cd++) { if (!clkdm1 || !clkdm2)
if (cd->clkdm) return -EINVAL;
continue;
cd->clkdm = _clkdm_lookup(cd->clkdm_name);
WARN(!cd->clkdm, "clockdomain: %s: could not find clkdm %s while resolving dependencies - should never happen", cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs);
clkdm->name, cd->clkdm_name); if (IS_ERR(cd))
ret = PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep)
ret = -EINVAL;
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
clkdm1->name, clkdm2->name);
return ret;
} }
cd->sleepdep_usecount--;
if (cd->sleepdep_usecount == 0) {
pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
}
return ret;
} }
/* Public functions */ /* Public functions */
...@@ -456,30 +572,18 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm) ...@@ -456,30 +572,18 @@ struct powerdomain *clkdm_get_pwrdm(struct clockdomain *clkdm)
int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
int ret = 0; int ret;
if (!clkdm1 || !clkdm2) if (!clkdm1 || !clkdm2)
return -EINVAL; return -EINVAL;
cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(cd)) if (IS_ERR(cd))
ret = PTR_ERR(cd); return PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_add_wkdep) pwrdm_lock(cd->clkdm->pwrdm.ptr);
ret = -EINVAL; ret = _clkdm_add_wkdep(clkdm1, clkdm2);
pwrdm_unlock(cd->clkdm->pwrdm.ptr);
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
return ret;
}
if (atomic_inc_return(&cd->wkdep_usecount) == 1) {
pr_debug("clockdomain: hardware will wake up %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_add_wkdep(clkdm1, clkdm2);
}
return ret; return ret;
} }
...@@ -497,30 +601,18 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) ...@@ -497,30 +601,18 @@ int clkdm_add_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) int clkdm_del_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
int ret = 0; int ret;
if (!clkdm1 || !clkdm2) if (!clkdm1 || !clkdm2)
return -EINVAL; return -EINVAL;
cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs); cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(cd)) if (IS_ERR(cd))
ret = PTR_ERR(cd); return PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_del_wkdep) pwrdm_lock(cd->clkdm->pwrdm.ptr);
ret = -EINVAL; ret = _clkdm_del_wkdep(clkdm1, clkdm2);
pwrdm_unlock(cd->clkdm->pwrdm.ptr);
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear wake up of %s when %s wakes up\n",
clkdm1->name, clkdm2->name);
return ret;
}
if (atomic_dec_return(&cd->wkdep_usecount) == 0) {
pr_debug("clockdomain: hardware will no longer wake up %s after %s wakes up\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_del_wkdep(clkdm1, clkdm2);
}
return ret; return ret;
} }
...@@ -560,7 +652,7 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) ...@@ -560,7 +652,7 @@ int clkdm_read_wkdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
return ret; return ret;
} }
/* XXX It's faster to return the atomic wkdep_usecount */ /* XXX It's faster to return the wkdep_usecount */
return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2); return arch_clkdm->clkdm_read_wkdep(clkdm1, clkdm2);
} }
...@@ -600,30 +692,18 @@ int clkdm_clear_all_wkdeps(struct clockdomain *clkdm) ...@@ -600,30 +692,18 @@ int clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
int ret = 0; int ret;
if (!clkdm1 || !clkdm2) if (!clkdm1 || !clkdm2)
return -EINVAL; return -EINVAL;
cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(cd)) if (IS_ERR(cd))
ret = PTR_ERR(cd); return PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_add_sleepdep) pwrdm_lock(cd->clkdm->pwrdm.ptr);
ret = -EINVAL; ret = _clkdm_add_sleepdep(clkdm1, clkdm2);
pwrdm_unlock(cd->clkdm->pwrdm.ptr);
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
clkdm1->name, clkdm2->name);
return ret;
}
if (atomic_inc_return(&cd->sleepdep_usecount) == 1) {
pr_debug("clockdomain: will prevent %s from sleeping if %s is active\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_add_sleepdep(clkdm1, clkdm2);
}
return ret; return ret;
} }
...@@ -643,30 +723,18 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) ...@@ -643,30 +723,18 @@ int clkdm_add_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
int ret = 0; int ret;
if (!clkdm1 || !clkdm2) if (!clkdm1 || !clkdm2)
return -EINVAL; return -EINVAL;
cd = _clkdm_deps_lookup(clkdm2, clkdm1->sleepdep_srcs); cd = _clkdm_deps_lookup(clkdm2, clkdm1->wkdep_srcs);
if (IS_ERR(cd)) if (IS_ERR(cd))
ret = PTR_ERR(cd); return PTR_ERR(cd);
if (!arch_clkdm || !arch_clkdm->clkdm_del_sleepdep) pwrdm_lock(cd->clkdm->pwrdm.ptr);
ret = -EINVAL; ret = _clkdm_del_sleepdep(clkdm1, clkdm2);
pwrdm_unlock(cd->clkdm->pwrdm.ptr);
if (ret) {
pr_debug("clockdomain: hardware cannot set/clear sleep dependency affecting %s from %s\n",
clkdm1->name, clkdm2->name);
return ret;
}
if (atomic_dec_return(&cd->sleepdep_usecount) == 0) {
pr_debug("clockdomain: will no longer prevent %s from sleeping if %s is active\n",
clkdm1->name, clkdm2->name);
ret = arch_clkdm->clkdm_del_sleepdep(clkdm1, clkdm2);
}
return ret; return ret;
} }
...@@ -708,7 +776,7 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2) ...@@ -708,7 +776,7 @@ int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2)
return ret; return ret;
} }
/* XXX It's faster to return the atomic sleepdep_usecount */ /* XXX It's faster to return the sleepdep_usecount */
return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2); return arch_clkdm->clkdm_read_sleepdep(clkdm1, clkdm2);
} }
...@@ -734,18 +802,17 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) ...@@ -734,18 +802,17 @@ int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
} }
/** /**
* clkdm_sleep - force clockdomain sleep transition * clkdm_sleep_nolock - force clockdomain sleep transition (lockless)
* @clkdm: struct clockdomain * * @clkdm: struct clockdomain *
* *
* Instruct the CM to force a sleep transition on the specified * Instruct the CM to force a sleep transition on the specified
* clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if * clockdomain @clkdm. Only for use by the powerdomain code. Returns
* clockdomain does not support software-initiated sleep; 0 upon * -EINVAL if @clkdm is NULL or if clockdomain does not support
* success. * software-initiated sleep; 0 upon success.
*/ */
int clkdm_sleep(struct clockdomain *clkdm) int clkdm_sleep_nolock(struct clockdomain *clkdm)
{ {
int ret; int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
...@@ -761,26 +828,45 @@ int clkdm_sleep(struct clockdomain *clkdm) ...@@ -761,26 +828,45 @@ int clkdm_sleep(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name); pr_debug("clockdomain: forcing sleep on %s\n", clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_sleep(clkdm); ret = arch_clkdm->clkdm_sleep(clkdm);
spin_unlock_irqrestore(&clkdm->lock, flags); ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
return ret; return ret;
} }
/** /**
* clkdm_wakeup - force clockdomain wakeup transition * clkdm_sleep - force clockdomain sleep transition
* @clkdm: struct clockdomain * * @clkdm: struct clockdomain *
* *
* Instruct the CM to force a wakeup transition on the specified * Instruct the CM to force a sleep transition on the specified
* clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if
* clockdomain does not support software-controlled wakeup; 0 upon * clockdomain does not support software-initiated sleep; 0 upon
* success. * success.
*/ */
int clkdm_wakeup(struct clockdomain *clkdm) int clkdm_sleep(struct clockdomain *clkdm)
{
int ret;
pwrdm_lock(clkdm->pwrdm.ptr);
ret = clkdm_sleep_nolock(clkdm);
pwrdm_unlock(clkdm->pwrdm.ptr);
return ret;
}
/**
* clkdm_wakeup_nolock - force clockdomain wakeup transition (lockless)
* @clkdm: struct clockdomain *
*
* Instruct the CM to force a wakeup transition on the specified
* clockdomain @clkdm. Only for use by the powerdomain code. Returns
* -EINVAL if @clkdm is NULL or if the clockdomain does not support
* software-controlled wakeup; 0 upon success.
*/
int clkdm_wakeup_nolock(struct clockdomain *clkdm)
{ {
int ret; int ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return -EINVAL; return -EINVAL;
...@@ -796,28 +882,46 @@ int clkdm_wakeup(struct clockdomain *clkdm) ...@@ -796,28 +882,46 @@ int clkdm_wakeup(struct clockdomain *clkdm)
pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name); pr_debug("clockdomain: forcing wakeup on %s\n", clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
ret = arch_clkdm->clkdm_wakeup(clkdm); ret = arch_clkdm->clkdm_wakeup(clkdm);
ret |= pwrdm_state_switch(clkdm->pwrdm.ptr); ret |= pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret; return ret;
} }
/** /**
* clkdm_allow_idle - enable hwsup idle transitions for clkdm * clkdm_wakeup - force clockdomain wakeup transition
* @clkdm: struct clockdomain * * @clkdm: struct clockdomain *
* *
* Allow the hardware to automatically switch the clockdomain @clkdm into * Instruct the CM to force a wakeup transition on the specified
* active or idle states, as needed by downstream clocks. If the * clockdomain @clkdm. Returns -EINVAL if @clkdm is NULL or if the
* clockdomain does not support software-controlled wakeup; 0 upon
* success.
*/
int clkdm_wakeup(struct clockdomain *clkdm)
{
int ret;
pwrdm_lock(clkdm->pwrdm.ptr);
ret = clkdm_wakeup_nolock(clkdm);
pwrdm_unlock(clkdm->pwrdm.ptr);
return ret;
}
/**
* clkdm_allow_idle_nolock - enable hwsup idle transitions for clkdm
* @clkdm: struct clockdomain *
*
* Allow the hardware to automatically switch the clockdomain @clkdm
* into active or idle states, as needed by downstream clocks. If the
* clockdomain has any downstream clocks enabled in the clock * clockdomain has any downstream clocks enabled in the clock
* framework, wkdep/sleepdep autodependencies are added; this is so * framework, wkdep/sleepdep autodependencies are added; this is so
* device drivers can read and write to the device. No return value. * device drivers can read and write to the device. Only for use by
* the powerdomain code. No return value.
*/ */
void clkdm_allow_idle(struct clockdomain *clkdm) void clkdm_allow_idle_nolock(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
...@@ -833,11 +937,26 @@ void clkdm_allow_idle(struct clockdomain *clkdm) ...@@ -833,11 +937,26 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: enabling automatic idle transitions for %s\n", pr_debug("clockdomain: enabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED; clkdm->_flags |= _CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_allow_idle(clkdm); arch_clkdm->clkdm_allow_idle(clkdm);
pwrdm_state_switch(clkdm->pwrdm.ptr); pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
spin_unlock_irqrestore(&clkdm->lock, flags); }
/**
* clkdm_allow_idle - enable hwsup idle transitions for clkdm
* @clkdm: struct clockdomain *
*
* Allow the hardware to automatically switch the clockdomain @clkdm into
* active or idle states, as needed by downstream clocks. If the
* clockdomain has any downstream clocks enabled in the clock
* framework, wkdep/sleepdep autodependencies are added; this is so
* device drivers can read and write to the device. No return value.
*/
void clkdm_allow_idle(struct clockdomain *clkdm)
{
pwrdm_lock(clkdm->pwrdm.ptr);
clkdm_allow_idle_nolock(clkdm);
pwrdm_unlock(clkdm->pwrdm.ptr);
} }
/** /**
...@@ -847,12 +966,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm) ...@@ -847,12 +966,11 @@ void clkdm_allow_idle(struct clockdomain *clkdm)
* Prevent the hardware from automatically switching the clockdomain * Prevent the hardware from automatically switching the clockdomain
* @clkdm into inactive or idle states. If the clockdomain has * @clkdm into inactive or idle states. If the clockdomain has
* downstream clocks enabled in the clock framework, wkdep/sleepdep * downstream clocks enabled in the clock framework, wkdep/sleepdep
* autodependencies are removed. No return value. * autodependencies are removed. Only for use by the powerdomain
* code. No return value.
*/ */
void clkdm_deny_idle(struct clockdomain *clkdm) void clkdm_deny_idle_nolock(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm) if (!clkdm)
return; return;
...@@ -868,11 +986,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -868,11 +986,25 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
pr_debug("clockdomain: disabling automatic idle transitions for %s\n", pr_debug("clockdomain: disabling automatic idle transitions for %s\n",
clkdm->name); clkdm->name);
spin_lock_irqsave(&clkdm->lock, flags);
clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED; clkdm->_flags &= ~_CLKDM_FLAG_HWSUP_ENABLED;
arch_clkdm->clkdm_deny_idle(clkdm); arch_clkdm->clkdm_deny_idle(clkdm);
pwrdm_state_switch(clkdm->pwrdm.ptr); pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
spin_unlock_irqrestore(&clkdm->lock, flags); }
/**
* clkdm_deny_idle - disable hwsup idle transitions for clkdm
* @clkdm: struct clockdomain *
*
* Prevent the hardware from automatically switching the clockdomain
* @clkdm into inactive or idle states. If the clockdomain has
* downstream clocks enabled in the clock framework, wkdep/sleepdep
* autodependencies are removed. No return value.
*/
void clkdm_deny_idle(struct clockdomain *clkdm)
{
pwrdm_lock(clkdm->pwrdm.ptr);
clkdm_deny_idle_nolock(clkdm);
pwrdm_unlock(clkdm->pwrdm.ptr);
} }
/** /**
...@@ -889,14 +1021,11 @@ void clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -889,14 +1021,11 @@ void clkdm_deny_idle(struct clockdomain *clkdm)
bool clkdm_in_hwsup(struct clockdomain *clkdm) bool clkdm_in_hwsup(struct clockdomain *clkdm)
{ {
bool ret; bool ret;
unsigned long flags;
if (!clkdm) if (!clkdm)
return false; return false;
spin_lock_irqsave(&clkdm->lock, flags);
ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false; ret = (clkdm->_flags & _CLKDM_FLAG_HWSUP_ENABLED) ? true : false;
spin_unlock_irqrestore(&clkdm->lock, flags);
return ret; return ret;
} }
...@@ -918,30 +1047,91 @@ bool clkdm_missing_idle_reporting(struct clockdomain *clkdm) ...@@ -918,30 +1047,91 @@ bool clkdm_missing_idle_reporting(struct clockdomain *clkdm)
return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false; return (clkdm->flags & CLKDM_MISSING_IDLE_REPORTING) ? true : false;
} }
/* Public autodep handling functions (deprecated) */
/**
* clkdm_add_autodeps - add auto sleepdeps/wkdeps to clkdm upon clock enable
* @clkdm: struct clockdomain *
*
* Add the "autodep" sleep & wakeup dependencies to clockdomain 'clkdm'
* in hardware-supervised mode. Meant to be called from clock framework
* when a clock inside clockdomain 'clkdm' is enabled. No return value.
*
* XXX autodeps are deprecated and should be removed at the earliest
* opportunity
*/
void clkdm_add_autodeps(struct clockdomain *clkdm)
{
struct clkdm_autodep *autodep;
if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
return;
for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
if (IS_ERR(autodep->clkdm.ptr))
continue;
pr_debug("clockdomain: %s: adding %s sleepdep/wkdep\n",
clkdm->name, autodep->clkdm.ptr->name);
_clkdm_add_sleepdep(clkdm, autodep->clkdm.ptr);
_clkdm_add_wkdep(clkdm, autodep->clkdm.ptr);
}
}
/**
* clkdm_del_autodeps - remove auto sleepdeps/wkdeps from clkdm
* @clkdm: struct clockdomain *
*
* Remove the "autodep" sleep & wakeup dependencies from clockdomain 'clkdm'
* in hardware-supervised mode. Meant to be called from clock framework
* when a clock inside clockdomain 'clkdm' is disabled. No return value.
*
* XXX autodeps are deprecated and should be removed at the earliest
* opportunity
*/
void clkdm_del_autodeps(struct clockdomain *clkdm)
{
struct clkdm_autodep *autodep;
if (!autodeps || clkdm->flags & CLKDM_NO_AUTODEPS)
return;
for (autodep = autodeps; autodep->clkdm.ptr; autodep++) {
if (IS_ERR(autodep->clkdm.ptr))
continue;
pr_debug("clockdomain: %s: removing %s sleepdep/wkdep\n",
clkdm->name, autodep->clkdm.ptr->name);
_clkdm_del_sleepdep(clkdm, autodep->clkdm.ptr);
_clkdm_del_wkdep(clkdm, autodep->clkdm.ptr);
}
}
/* Clockdomain-to-clock/hwmod framework interface code */ /* Clockdomain-to-clock/hwmod framework interface code */
static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm) static int _clkdm_clk_hwmod_enable(struct clockdomain *clkdm)
{ {
unsigned long flags;
if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable) if (!clkdm || !arch_clkdm || !arch_clkdm->clkdm_clk_enable)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&clkdm->lock, flags); pwrdm_lock(clkdm->pwrdm.ptr);
/* /*
* For arch's with no autodeps, clkcm_clk_enable * For arch's with no autodeps, clkcm_clk_enable
* should be called for every clock instance or hwmod that is * should be called for every clock instance or hwmod that is
* enabled, so the clkdm can be force woken up. * enabled, so the clkdm can be force woken up.
*/ */
if ((atomic_inc_return(&clkdm->usecount) > 1) && autodeps) { clkdm->usecount++;
spin_unlock_irqrestore(&clkdm->lock, flags); if (clkdm->usecount > 1 && autodeps) {
pwrdm_unlock(clkdm->pwrdm.ptr);
return 0; return 0;
} }
arch_clkdm->clkdm_clk_enable(clkdm); arch_clkdm->clkdm_clk_enable(clkdm);
pwrdm_state_switch(clkdm->pwrdm.ptr); pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
spin_unlock_irqrestore(&clkdm->lock, flags); pwrdm_unlock(clkdm->pwrdm.ptr);
pr_debug("clockdomain: %s: enabled\n", clkdm->name); pr_debug("clockdomain: %s: enabled\n", clkdm->name);
...@@ -990,36 +1180,34 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk) ...@@ -990,36 +1180,34 @@ int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk)
*/ */
int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk) int clkdm_clk_disable(struct clockdomain *clkdm, struct clk *clk)
{ {
unsigned long flags;
if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) if (!clkdm || !clk || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&clkdm->lock, flags); pwrdm_lock(clkdm->pwrdm.ptr);
/* corner case: disabling unused clocks */ /* corner case: disabling unused clocks */
if ((__clk_get_enable_count(clk) == 0) && if ((__clk_get_enable_count(clk) == 0) && clkdm->usecount == 0)
(atomic_read(&clkdm->usecount) == 0))
goto ccd_exit; goto ccd_exit;
if (atomic_read(&clkdm->usecount) == 0) { if (clkdm->usecount == 0) {
spin_unlock_irqrestore(&clkdm->lock, flags); pwrdm_unlock(clkdm->pwrdm.ptr);
WARN_ON(1); /* underflow */ WARN_ON(1); /* underflow */
return -ERANGE; return -ERANGE;
} }
if (atomic_dec_return(&clkdm->usecount) > 0) { clkdm->usecount--;
spin_unlock_irqrestore(&clkdm->lock, flags); if (clkdm->usecount > 0) {
pwrdm_unlock(clkdm->pwrdm.ptr);
return 0; return 0;
} }
arch_clkdm->clkdm_clk_disable(clkdm); arch_clkdm->clkdm_clk_disable(clkdm);
pwrdm_state_switch(clkdm->pwrdm.ptr); pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
pr_debug("clockdomain: %s: disabled\n", clkdm->name); pr_debug("clockdomain: %s: disabled\n", clkdm->name);
ccd_exit: ccd_exit:
spin_unlock_irqrestore(&clkdm->lock, flags); pwrdm_unlock(clkdm->pwrdm.ptr);
return 0; return 0;
} }
...@@ -1072,8 +1260,6 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh) ...@@ -1072,8 +1260,6 @@ int clkdm_hwmod_enable(struct clockdomain *clkdm, struct omap_hwmod *oh)
*/ */
int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
{ {
unsigned long flags;
/* The clkdm attribute does not exist yet prior OMAP4 */ /* The clkdm attribute does not exist yet prior OMAP4 */
if (cpu_is_omap24xx() || cpu_is_omap34xx()) if (cpu_is_omap24xx() || cpu_is_omap34xx())
return 0; return 0;
...@@ -1086,22 +1272,23 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh) ...@@ -1086,22 +1272,23 @@ int clkdm_hwmod_disable(struct clockdomain *clkdm, struct omap_hwmod *oh)
if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable) if (!clkdm || !oh || !arch_clkdm || !arch_clkdm->clkdm_clk_disable)
return -EINVAL; return -EINVAL;
spin_lock_irqsave(&clkdm->lock, flags); pwrdm_lock(clkdm->pwrdm.ptr);
if (atomic_read(&clkdm->usecount) == 0) { if (clkdm->usecount == 0) {
spin_unlock_irqrestore(&clkdm->lock, flags); pwrdm_unlock(clkdm->pwrdm.ptr);
WARN_ON(1); /* underflow */ WARN_ON(1); /* underflow */
return -ERANGE; return -ERANGE;
} }
if (atomic_dec_return(&clkdm->usecount) > 0) { clkdm->usecount--;
spin_unlock_irqrestore(&clkdm->lock, flags); if (clkdm->usecount > 0) {
pwrdm_unlock(clkdm->pwrdm.ptr);
return 0; return 0;
} }
arch_clkdm->clkdm_clk_disable(clkdm); arch_clkdm->clkdm_clk_disable(clkdm);
pwrdm_state_switch(clkdm->pwrdm.ptr); pwrdm_state_switch_nolock(clkdm->pwrdm.ptr);
spin_unlock_irqrestore(&clkdm->lock, flags); pwrdm_unlock(clkdm->pwrdm.ptr);
pr_debug("clockdomain: %s: disabled\n", clkdm->name); pr_debug("clockdomain: %s: disabled\n", clkdm->name);
......
...@@ -15,7 +15,6 @@ ...@@ -15,7 +15,6 @@
#define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H #define __ARCH_ARM_MACH_OMAP2_CLOCKDOMAIN_H
#include <linux/init.h> #include <linux/init.h>
#include <linux/spinlock.h>
#include "powerdomain.h" #include "powerdomain.h"
#include "clock.h" #include "clock.h"
...@@ -92,8 +91,8 @@ struct clkdm_autodep { ...@@ -92,8 +91,8 @@ struct clkdm_autodep {
struct clkdm_dep { struct clkdm_dep {
const char *clkdm_name; const char *clkdm_name;
struct clockdomain *clkdm; struct clockdomain *clkdm;
atomic_t wkdep_usecount; s16 wkdep_usecount;
atomic_t sleepdep_usecount; s16 sleepdep_usecount;
}; };
/* Possible flags for struct clockdomain._flags */ /* Possible flags for struct clockdomain._flags */
...@@ -137,9 +136,8 @@ struct clockdomain { ...@@ -137,9 +136,8 @@ struct clockdomain {
const u16 clkdm_offs; const u16 clkdm_offs;
struct clkdm_dep *wkdep_srcs; struct clkdm_dep *wkdep_srcs;
struct clkdm_dep *sleepdep_srcs; struct clkdm_dep *sleepdep_srcs;
atomic_t usecount; int usecount;
struct list_head node; struct list_head node;
spinlock_t lock;
}; };
/** /**
...@@ -196,12 +194,16 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); ...@@ -196,12 +194,16 @@ int clkdm_del_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);
int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2); int clkdm_read_sleepdep(struct clockdomain *clkdm1, struct clockdomain *clkdm2);
int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm); int clkdm_clear_all_sleepdeps(struct clockdomain *clkdm);
void clkdm_allow_idle_nolock(struct clockdomain *clkdm);
void clkdm_allow_idle(struct clockdomain *clkdm); void clkdm_allow_idle(struct clockdomain *clkdm);
void clkdm_deny_idle_nolock(struct clockdomain *clkdm);
void clkdm_deny_idle(struct clockdomain *clkdm); void clkdm_deny_idle(struct clockdomain *clkdm);
bool clkdm_in_hwsup(struct clockdomain *clkdm); bool clkdm_in_hwsup(struct clockdomain *clkdm);
bool clkdm_missing_idle_reporting(struct clockdomain *clkdm); bool clkdm_missing_idle_reporting(struct clockdomain *clkdm);
int clkdm_wakeup_nolock(struct clockdomain *clkdm);
int clkdm_wakeup(struct clockdomain *clkdm); int clkdm_wakeup(struct clockdomain *clkdm);
int clkdm_sleep_nolock(struct clockdomain *clkdm);
int clkdm_sleep(struct clockdomain *clkdm); int clkdm_sleep(struct clockdomain *clkdm);
int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk); int clkdm_clk_enable(struct clockdomain *clkdm, struct clk *clk);
...@@ -214,8 +216,9 @@ extern void __init omap243x_clockdomains_init(void); ...@@ -214,8 +216,9 @@ extern void __init omap243x_clockdomains_init(void);
extern void __init omap3xxx_clockdomains_init(void); extern void __init omap3xxx_clockdomains_init(void);
extern void __init am33xx_clockdomains_init(void); extern void __init am33xx_clockdomains_init(void);
extern void __init omap44xx_clockdomains_init(void); extern void __init omap44xx_clockdomains_init(void);
extern void _clkdm_add_autodeps(struct clockdomain *clkdm);
extern void _clkdm_del_autodeps(struct clockdomain *clkdm); extern void clkdm_add_autodeps(struct clockdomain *clkdm);
extern void clkdm_del_autodeps(struct clockdomain *clkdm);
extern struct clkdm_ops omap2_clkdm_operations; extern struct clkdm_ops omap2_clkdm_operations;
extern struct clkdm_ops omap3_clkdm_operations; extern struct clkdm_ops omap3_clkdm_operations;
......
...@@ -273,9 +273,6 @@ int omap2xxx_cm_wait_module_ready(s16 prcm_mod, u8 idlest_id, u8 idlest_shift) ...@@ -273,9 +273,6 @@ int omap2xxx_cm_wait_module_ready(s16 prcm_mod, u8 idlest_id, u8 idlest_shift)
static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm) static void omap2xxx_clkdm_allow_idle(struct clockdomain *clkdm)
{ {
if (atomic_read(&clkdm->usecount) > 0)
_clkdm_add_autodeps(clkdm);
omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
} }
...@@ -284,9 +281,6 @@ static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -284,9 +281,6 @@ static void omap2xxx_clkdm_deny_idle(struct clockdomain *clkdm)
{ {
omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
if (atomic_read(&clkdm->usecount) > 0)
_clkdm_del_autodeps(clkdm);
} }
static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm) static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm)
...@@ -298,18 +292,8 @@ static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm) ...@@ -298,18 +292,8 @@ static int omap2xxx_clkdm_clk_enable(struct clockdomain *clkdm)
hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
if (hwsup) { omap2xxx_clkdm_wakeup(clkdm);
/* Disable HW transitions when we are changing deps */
omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask);
_clkdm_add_autodeps(clkdm);
omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask);
} else {
if (clkdm->flags & CLKDM_CAN_FORCE_WAKEUP)
omap2xxx_clkdm_wakeup(clkdm);
}
return 0; return 0;
} }
...@@ -324,17 +308,8 @@ static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm) ...@@ -324,17 +308,8 @@ static int omap2xxx_clkdm_clk_disable(struct clockdomain *clkdm)
hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs, hwsup = omap2xxx_cm_is_clkdm_in_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
if (hwsup) { if (!hwsup && clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
/* Disable HW transitions when we are changing deps */ omap2xxx_clkdm_sleep(clkdm);
omap2xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask);
_clkdm_del_autodeps(clkdm);
omap2xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask);
} else {
if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP)
omap2xxx_clkdm_sleep(clkdm);
}
return 0; return 0;
} }
......
...@@ -186,7 +186,7 @@ static int omap3xxx_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm) ...@@ -186,7 +186,7 @@ static int omap3xxx_clkdm_clear_all_sleepdeps(struct clockdomain *clkdm)
continue; /* only happens if data is erroneous */ continue; /* only happens if data is erroneous */
mask |= 1 << cd->clkdm->dep_bit; mask |= 1 << cd->clkdm->dep_bit;
atomic_set(&cd->sleepdep_usecount, 0); cd->sleepdep_usecount = 0;
} }
omap2_cm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, omap2_cm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
OMAP3430_CM_SLEEPDEP); OMAP3430_CM_SLEEPDEP);
...@@ -209,8 +209,8 @@ static int omap3xxx_clkdm_wakeup(struct clockdomain *clkdm) ...@@ -209,8 +209,8 @@ static int omap3xxx_clkdm_wakeup(struct clockdomain *clkdm)
static void omap3xxx_clkdm_allow_idle(struct clockdomain *clkdm) static void omap3xxx_clkdm_allow_idle(struct clockdomain *clkdm)
{ {
if (atomic_read(&clkdm->usecount) > 0) if (clkdm->usecount > 0)
_clkdm_add_autodeps(clkdm); clkdm_add_autodeps(clkdm);
omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
...@@ -221,8 +221,8 @@ static void omap3xxx_clkdm_deny_idle(struct clockdomain *clkdm) ...@@ -221,8 +221,8 @@ static void omap3xxx_clkdm_deny_idle(struct clockdomain *clkdm)
omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
if (atomic_read(&clkdm->usecount) > 0) if (clkdm->usecount > 0)
_clkdm_del_autodeps(clkdm); clkdm_del_autodeps(clkdm);
} }
static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm) static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
...@@ -250,7 +250,7 @@ static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm) ...@@ -250,7 +250,7 @@ static int omap3xxx_clkdm_clk_enable(struct clockdomain *clkdm)
/* Disable HW transitions when we are changing deps */ /* Disable HW transitions when we are changing deps */
omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
_clkdm_add_autodeps(clkdm); clkdm_add_autodeps(clkdm);
omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
} else { } else {
...@@ -287,7 +287,7 @@ static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm) ...@@ -287,7 +287,7 @@ static int omap3xxx_clkdm_clk_disable(struct clockdomain *clkdm)
/* Disable HW transitions when we are changing deps */ /* Disable HW transitions when we are changing deps */
omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_disable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
_clkdm_del_autodeps(clkdm); clkdm_del_autodeps(clkdm);
omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs, omap3xxx_cm_clkdm_enable_hwsup(clkdm->pwrdm.ptr->prcm_offs,
clkdm->clktrctrl_mask); clkdm->clktrctrl_mask);
} else { } else {
......
...@@ -393,7 +393,7 @@ static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm) ...@@ -393,7 +393,7 @@ static int omap4_clkdm_clear_all_wkup_sleep_deps(struct clockdomain *clkdm)
continue; /* only happens if data is erroneous */ continue; /* only happens if data is erroneous */
mask |= 1 << cd->clkdm->dep_bit; mask |= 1 << cd->clkdm->dep_bit;
atomic_set(&cd->wkdep_usecount, 0); cd->wkdep_usecount = 0;
} }
omap4_cminst_clear_inst_reg_bits(mask, clkdm->prcm_partition, omap4_cminst_clear_inst_reg_bits(mask, clkdm->prcm_partition,
......
...@@ -36,40 +36,66 @@ ...@@ -36,40 +36,66 @@
/* Mach specific information to be recorded in the C-state driver_data */ /* Mach specific information to be recorded in the C-state driver_data */
struct omap3_idle_statedata { struct omap3_idle_statedata {
u32 mpu_state; u8 mpu_state;
u32 core_state; u8 core_state;
u8 per_min_state;
u8 flags;
}; };
static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd; static struct powerdomain *mpu_pd, *core_pd, *per_pd, *cam_pd;
/*
* Possible flag bits for struct omap3_idle_statedata.flags:
*
* OMAP_CPUIDLE_CX_NO_CLKDM_IDLE: don't allow the MPU clockdomain to go
* inactive. This in turn prevents the MPU DPLL from entering autoidle
* mode, so wakeup latency is greatly reduced, at the cost of additional
* energy consumption. This also prevents the CORE clockdomain from
* entering idle.
*/
#define OMAP_CPUIDLE_CX_NO_CLKDM_IDLE BIT(0)
/*
* Prevent PER OFF if CORE is not in RETention or OFF as this would
* disable PER wakeups completely.
*/
static struct omap3_idle_statedata omap3_idle_data[] = { static struct omap3_idle_statedata omap3_idle_data[] = {
{ {
.mpu_state = PWRDM_POWER_ON, .mpu_state = PWRDM_POWER_ON,
.core_state = PWRDM_POWER_ON, .core_state = PWRDM_POWER_ON,
/* In C1 do not allow PER state lower than CORE state */
.per_min_state = PWRDM_POWER_ON,
.flags = OMAP_CPUIDLE_CX_NO_CLKDM_IDLE,
}, },
{ {
.mpu_state = PWRDM_POWER_ON, .mpu_state = PWRDM_POWER_ON,
.core_state = PWRDM_POWER_ON, .core_state = PWRDM_POWER_ON,
.per_min_state = PWRDM_POWER_RET,
}, },
{ {
.mpu_state = PWRDM_POWER_RET, .mpu_state = PWRDM_POWER_RET,
.core_state = PWRDM_POWER_ON, .core_state = PWRDM_POWER_ON,
.per_min_state = PWRDM_POWER_RET,
}, },
{ {
.mpu_state = PWRDM_POWER_OFF, .mpu_state = PWRDM_POWER_OFF,
.core_state = PWRDM_POWER_ON, .core_state = PWRDM_POWER_ON,
.per_min_state = PWRDM_POWER_RET,
}, },
{ {
.mpu_state = PWRDM_POWER_RET, .mpu_state = PWRDM_POWER_RET,
.core_state = PWRDM_POWER_RET, .core_state = PWRDM_POWER_RET,
.per_min_state = PWRDM_POWER_OFF,
}, },
{ {
.mpu_state = PWRDM_POWER_OFF, .mpu_state = PWRDM_POWER_OFF,
.core_state = PWRDM_POWER_RET, .core_state = PWRDM_POWER_RET,
.per_min_state = PWRDM_POWER_OFF,
}, },
{ {
.mpu_state = PWRDM_POWER_OFF, .mpu_state = PWRDM_POWER_OFF,
.core_state = PWRDM_POWER_OFF, .core_state = PWRDM_POWER_OFF,
.per_min_state = PWRDM_POWER_OFF,
}, },
}; };
...@@ -80,27 +106,25 @@ static int __omap3_enter_idle(struct cpuidle_device *dev, ...@@ -80,27 +106,25 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
int index) int index)
{ {
struct omap3_idle_statedata *cx = &omap3_idle_data[index]; struct omap3_idle_statedata *cx = &omap3_idle_data[index];
u32 mpu_state = cx->mpu_state, core_state = cx->core_state;
local_fiq_disable(); local_fiq_disable();
pwrdm_set_next_pwrst(mpu_pd, mpu_state);
pwrdm_set_next_pwrst(core_pd, core_state);
if (omap_irq_pending() || need_resched()) if (omap_irq_pending() || need_resched())
goto return_sleep_time; goto return_sleep_time;
/* Deny idle for C1 */ /* Deny idle for C1 */
if (index == 0) { if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE) {
clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]); clkdm_deny_idle(mpu_pd->pwrdm_clkdms[0]);
clkdm_deny_idle(core_pd->pwrdm_clkdms[0]); } else {
pwrdm_set_next_pwrst(mpu_pd, cx->mpu_state);
pwrdm_set_next_pwrst(core_pd, cx->core_state);
} }
/* /*
* Call idle CPU PM enter notifier chain so that * Call idle CPU PM enter notifier chain so that
* VFP context is saved. * VFP context is saved.
*/ */
if (mpu_state == PWRDM_POWER_OFF) if (cx->mpu_state == PWRDM_POWER_OFF)
cpu_pm_enter(); cpu_pm_enter();
/* Execute ARM wfi */ /* Execute ARM wfi */
...@@ -110,17 +134,15 @@ static int __omap3_enter_idle(struct cpuidle_device *dev, ...@@ -110,17 +134,15 @@ static int __omap3_enter_idle(struct cpuidle_device *dev,
* Call idle CPU PM enter notifier chain to restore * Call idle CPU PM enter notifier chain to restore
* VFP context. * VFP context.
*/ */
if (pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF) if (cx->mpu_state == PWRDM_POWER_OFF &&
pwrdm_read_prev_pwrst(mpu_pd) == PWRDM_POWER_OFF)
cpu_pm_exit(); cpu_pm_exit();
/* Re-allow idle for C1 */ /* Re-allow idle for C1 */
if (index == 0) { if (cx->flags & OMAP_CPUIDLE_CX_NO_CLKDM_IDLE)
clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]); clkdm_allow_idle(mpu_pd->pwrdm_clkdms[0]);
clkdm_allow_idle(core_pd->pwrdm_clkdms[0]);
}
return_sleep_time: return_sleep_time:
local_fiq_enable(); local_fiq_enable();
return index; return index;
...@@ -185,7 +207,7 @@ static int next_valid_state(struct cpuidle_device *dev, ...@@ -185,7 +207,7 @@ static int next_valid_state(struct cpuidle_device *dev,
* Start search from the next (lower) state. * Start search from the next (lower) state.
*/ */
for (idx = index - 1; idx >= 0; idx--) { for (idx = index - 1; idx >= 0; idx--) {
cx = &omap3_idle_data[idx]; cx = &omap3_idle_data[idx];
if ((cx->mpu_state >= mpu_deepest_state) && if ((cx->mpu_state >= mpu_deepest_state) &&
(cx->core_state >= core_deepest_state)) { (cx->core_state >= core_deepest_state)) {
next_index = idx; next_index = idx;
...@@ -209,10 +231,9 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, ...@@ -209,10 +231,9 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
struct cpuidle_driver *drv, struct cpuidle_driver *drv,
int index) int index)
{ {
int new_state_idx; int new_state_idx, ret;
u32 core_next_state, per_next_state = 0, per_saved_state = 0; u8 per_next_state, per_saved_state;
struct omap3_idle_statedata *cx; struct omap3_idle_statedata *cx;
int ret;
/* /*
* Use only C1 if CAM is active. * Use only C1 if CAM is active.
...@@ -233,25 +254,13 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev, ...@@ -233,25 +254,13 @@ static int omap3_enter_idle_bm(struct cpuidle_device *dev,
/* Program PER state */ /* Program PER state */
cx = &omap3_idle_data[new_state_idx]; cx = &omap3_idle_data[new_state_idx];
core_next_state = cx->core_state;
per_next_state = per_saved_state = pwrdm_read_next_pwrst(per_pd);
if (new_state_idx == 0) {
/* In C1 do not allow PER state lower than CORE state */
if (per_next_state < core_next_state)
per_next_state = core_next_state;
} else {
/*
* Prevent PER OFF if CORE is not in RETention or OFF as this
* would disable PER wakeups completely.
*/
if ((per_next_state == PWRDM_POWER_OFF) &&
(core_next_state > PWRDM_POWER_RET))
per_next_state = PWRDM_POWER_RET;
}
/* Are we changing PER target state? */ per_next_state = pwrdm_read_next_pwrst(per_pd);
if (per_next_state != per_saved_state) per_saved_state = per_next_state;
if (per_next_state < cx->per_min_state) {
per_next_state = cx->per_min_state;
pwrdm_set_next_pwrst(per_pd, per_next_state); pwrdm_set_next_pwrst(per_pd, per_next_state);
}
ret = omap3_enter_idle(dev, drv, new_state_idx); ret = omap3_enter_idle(dev, drv, new_state_idx);
......
...@@ -86,37 +86,6 @@ static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr) ...@@ -86,37 +86,6 @@ static inline void set_cpu_wakeup_addr(unsigned int cpu_id, u32 addr)
__raw_writel(addr, pm_info->wkup_sar_addr); __raw_writel(addr, pm_info->wkup_sar_addr);
} }
/*
* Set the CPUx powerdomain's previous power state
*/
static inline void set_cpu_next_pwrst(unsigned int cpu_id,
unsigned int power_state)
{
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
}
/*
* Read CPU's previous power state
*/
static inline unsigned int read_cpu_prev_pwrst(unsigned int cpu_id)
{
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
return pwrdm_read_prev_pwrst(pm_info->pwrdm);
}
/*
* Clear the CPUx powerdomain's previous power state
*/
static inline void clear_cpu_prev_pwrst(unsigned int cpu_id)
{
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu_id);
pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
}
/* /*
* Store the SCU power status value to scratchpad memory * Store the SCU power status value to scratchpad memory
*/ */
...@@ -230,6 +199,7 @@ static void save_l2x0_context(void) ...@@ -230,6 +199,7 @@ static void save_l2x0_context(void)
*/ */
int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
{ {
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
unsigned int save_state = 0; unsigned int save_state = 0;
unsigned int wakeup_cpu; unsigned int wakeup_cpu;
...@@ -268,7 +238,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) ...@@ -268,7 +238,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
save_state = 2; save_state = 2;
cpu_clear_prev_logic_pwrst(cpu); cpu_clear_prev_logic_pwrst(cpu);
set_cpu_next_pwrst(cpu, power_state); pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume)); set_cpu_wakeup_addr(cpu, virt_to_phys(omap4_cpu_resume));
scu_pwrst_prepare(cpu, power_state); scu_pwrst_prepare(cpu, power_state);
l2x0_pwrst_prepare(cpu, save_state); l2x0_pwrst_prepare(cpu, save_state);
...@@ -286,7 +256,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) ...@@ -286,7 +256,7 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
* domain transition * domain transition
*/ */
wakeup_cpu = smp_processor_id(); wakeup_cpu = smp_processor_id();
set_cpu_next_pwrst(wakeup_cpu, PWRDM_POWER_ON); pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
pwrdm_post_transition(NULL); pwrdm_post_transition(NULL);
...@@ -300,8 +270,8 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state) ...@@ -300,8 +270,8 @@ int omap4_enter_lowpower(unsigned int cpu, unsigned int power_state)
*/ */
int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
{ {
unsigned int cpu_state = 0;
struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu); struct omap4_cpu_pm_info *pm_info = &per_cpu(omap4_pm_info, cpu);
unsigned int cpu_state = 0;
if (omap_rev() == OMAP4430_REV_ES1_0) if (omap_rev() == OMAP4430_REV_ES1_0)
return -ENXIO; return -ENXIO;
...@@ -309,8 +279,8 @@ int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) ...@@ -309,8 +279,8 @@ int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
if (power_state == PWRDM_POWER_OFF) if (power_state == PWRDM_POWER_OFF)
cpu_state = 1; cpu_state = 1;
clear_cpu_prev_pwrst(cpu); pwrdm_clear_all_prev_pwrst(pm_info->pwrdm);
set_cpu_next_pwrst(cpu, power_state); pwrdm_set_next_pwrst(pm_info->pwrdm, power_state);
set_cpu_wakeup_addr(cpu, virt_to_phys(pm_info->secondary_startup)); set_cpu_wakeup_addr(cpu, virt_to_phys(pm_info->secondary_startup));
scu_pwrst_prepare(cpu, power_state); scu_pwrst_prepare(cpu, power_state);
...@@ -321,7 +291,7 @@ int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state) ...@@ -321,7 +291,7 @@ int __cpuinit omap4_hotplug_cpu(unsigned int cpu, unsigned int power_state)
*/ */
omap4_finish_suspend(cpu_state); omap4_finish_suspend(cpu_state);
set_cpu_next_pwrst(cpu, PWRDM_POWER_ON); pwrdm_set_next_pwrst(pm_info->pwrdm, PWRDM_POWER_ON);
return 0; return 0;
} }
......
...@@ -83,10 +83,8 @@ static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user) ...@@ -83,10 +83,8 @@ static int clkdm_dbg_show_counter(struct clockdomain *clkdm, void *user)
strncmp(clkdm->name, "dpll", 4) == 0) strncmp(clkdm->name, "dpll", 4) == 0)
return 0; return 0;
seq_printf(s, "%s->%s (%d)", clkdm->name, seq_printf(s, "%s->%s (%d)\n", clkdm->name, clkdm->pwrdm.ptr->name,
clkdm->pwrdm.ptr->name, clkdm->usecount);
atomic_read(&clkdm->usecount));
seq_printf(s, "\n");
return 0; return 0;
} }
......
...@@ -106,79 +106,18 @@ static void __init omap2_init_processor_devices(void) ...@@ -106,79 +106,18 @@ static void __init omap2_init_processor_devices(void)
} }
} }
/* Types of sleep_switch used in omap_set_pwrdm_state */
#define FORCEWAKEUP_SWITCH 0
#define LOWPOWERSTATE_SWITCH 1
int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
{ {
/* XXX The usecount test is racy */
if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) && if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
!(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING)) !(clkdm->flags & CLKDM_MISSING_IDLE_REPORTING))
clkdm_allow_idle(clkdm); clkdm_allow_idle(clkdm);
else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP && else if (clkdm->flags & CLKDM_CAN_FORCE_SLEEP &&
atomic_read(&clkdm->usecount) == 0) clkdm->usecount == 0)
clkdm_sleep(clkdm); clkdm_sleep(clkdm);
return 0; return 0;
} }
/*
* This sets pwrdm state (other than mpu & core. Currently only ON &
* RET are supported.
*/
int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 pwrst)
{
u8 curr_pwrst, next_pwrst;
int sleep_switch = -1, ret = 0, hwsup = 0;
if (!pwrdm || IS_ERR(pwrdm))
return -EINVAL;
while (!(pwrdm->pwrsts & (1 << pwrst))) {
if (pwrst == PWRDM_POWER_OFF)
return ret;
pwrst--;
}
next_pwrst = pwrdm_read_next_pwrst(pwrdm);
if (next_pwrst == pwrst)
return ret;
curr_pwrst = pwrdm_read_pwrst(pwrdm);
if (curr_pwrst < PWRDM_POWER_ON) {
if ((curr_pwrst > pwrst) &&
(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, pwrst);
if (ret)
pr_err("%s: unable to set power state of powerdomain: %s\n",
__func__, pwrdm->name);
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);
pwrdm_wait_transition(pwrdm);
pwrdm_state_switch(pwrdm);
break;
}
return ret;
}
/* /*
* This API is to be called during init to set the various voltage * 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 * domains to the voltage as per the opp table. Typically we boot up
......
...@@ -33,7 +33,6 @@ static inline int omap4_idle_init(void) ...@@ -33,7 +33,6 @@ static inline int omap4_idle_init(void)
extern void *omap3_secure_ram_storage; extern void *omap3_secure_ram_storage;
extern void omap3_pm_off_mode_enable(int); extern void omap3_pm_off_mode_enable(int);
extern void omap_sram_idle(void); extern void omap_sram_idle(void);
extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused);
extern int (*omap_pm_suspend)(void); extern int (*omap_pm_suspend)(void);
......
...@@ -90,11 +90,7 @@ static int omap2_enter_full_retention(void) ...@@ -90,11 +90,7 @@ static int omap2_enter_full_retention(void)
omap2_prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2); omap2_prm_write_mod_reg(0xffffffff, CORE_MOD, OMAP24XX_PM_WKST2);
omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST); omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
/* pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_RET);
* Set MPU powerdomain's next power state to RETENTION;
* preserve logic state during retention
*/
pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET); pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
/* Workaround to kill USB */ /* Workaround to kill USB */
...@@ -137,6 +133,9 @@ static int omap2_enter_full_retention(void) ...@@ -137,6 +133,9 @@ static int omap2_enter_full_retention(void)
/* Mask future PRCM-to-MPU interrupts */ /* Mask future PRCM-to-MPU interrupts */
omap2_prm_write_mod_reg(0x0, OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET); omap2_prm_write_mod_reg(0x0, OCP_MOD, OMAP2_PRCM_IRQSTATUS_MPU_OFFSET);
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_ON);
return 0; return 0;
} }
...@@ -173,17 +172,16 @@ static void omap2_enter_mpu_retention(void) ...@@ -173,17 +172,16 @@ static void omap2_enter_mpu_retention(void)
omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST); omap2_prm_write_mod_reg(0xffffffff, WKUP_MOD, PM_WKST);
/* Try to enter MPU retention */ /* Try to enter MPU retention */
omap2_prm_write_mod_reg((0x01 << OMAP_POWERSTATE_SHIFT) | pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
OMAP_LOGICRETSTATE_MASK,
MPU_MOD, OMAP2_PM_PWSTCTRL);
} else { } else {
/* Block MPU retention */ /* Block MPU retention */
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
omap2_prm_write_mod_reg(OMAP_LOGICRETSTATE_MASK, MPU_MOD,
OMAP2_PM_PWSTCTRL);
} }
omap2_sram_idle(); omap2_sram_idle();
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_ON);
} }
static int omap2_can_sleep(void) static int omap2_can_sleep(void)
...@@ -238,25 +236,17 @@ static void __init prcm_setup_regs(void) ...@@ -238,25 +236,17 @@ static void __init prcm_setup_regs(void)
for (i = 0; i < num_mem_banks; i++) for (i = 0; i < num_mem_banks; i++)
pwrdm_set_mem_retst(core_pwrdm, i, PWRDM_POWER_RET); pwrdm_set_mem_retst(core_pwrdm, i, PWRDM_POWER_RET);
/* Set CORE powerdomain's next power state to RETENTION */ pwrdm_set_logic_retst(core_pwrdm, PWRDM_POWER_RET);
pwrdm_set_next_pwrst(core_pwrdm, PWRDM_POWER_RET);
/*
* Set MPU powerdomain's next power state to RETENTION;
* preserve logic state during retention
*/
pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET); pwrdm_set_logic_retst(mpu_pwrdm, PWRDM_POWER_RET);
pwrdm_set_next_pwrst(mpu_pwrdm, PWRDM_POWER_RET);
/* Force-power down DSP, GFX powerdomains */ /* Force-power down DSP, GFX powerdomains */
pwrdm = clkdm_get_pwrdm(dsp_clkdm); pwrdm = clkdm_get_pwrdm(dsp_clkdm);
pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF); pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
clkdm_sleep(dsp_clkdm);
pwrdm = clkdm_get_pwrdm(gfx_clkdm); pwrdm = clkdm_get_pwrdm(gfx_clkdm);
pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF); pwrdm_set_next_pwrst(pwrdm, PWRDM_POWER_OFF);
clkdm_sleep(gfx_clkdm);
/* Enable hardware-supervised idle for all clkdms */ /* Enable hardware-supervised idle for all clkdms */
clkdm_for_each(omap_pm_clkdms_setup, NULL); clkdm_for_each(omap_pm_clkdms_setup, NULL);
......
...@@ -19,6 +19,7 @@ ...@@ -19,6 +19,7 @@
#include <linux/list.h> #include <linux/list.h>
#include <linux/errno.h> #include <linux/errno.h>
#include <linux/string.h> #include <linux/string.h>
#include <linux/spinlock.h>
#include <trace/events/power.h> #include <trace/events/power.h>
#include "cm2xxx_3xxx.h" #include "cm2xxx_3xxx.h"
...@@ -42,6 +43,16 @@ enum { ...@@ -42,6 +43,16 @@ enum {
PWRDM_STATE_PREV, PWRDM_STATE_PREV,
}; };
/*
* Types of sleep_switch used internally in omap_set_pwrdm_state()
* and its associated static functions
*
* XXX Better documentation is needed here
*/
#define ALREADYACTIVE_SWITCH 0
#define FORCEWAKEUP_SWITCH 1
#define LOWPOWERSTATE_SWITCH 2
#define ERROR_SWITCH 3
/* pwrdm_list contains all registered struct powerdomains */ /* pwrdm_list contains all registered struct powerdomains */
static LIST_HEAD(pwrdm_list); static LIST_HEAD(pwrdm_list);
...@@ -101,6 +112,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm) ...@@ -101,6 +112,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
pwrdm->voltdm.ptr = voltdm; pwrdm->voltdm.ptr = voltdm;
INIT_LIST_HEAD(&pwrdm->voltdm_node); INIT_LIST_HEAD(&pwrdm->voltdm_node);
voltdm_add_pwrdm(voltdm, pwrdm); voltdm_add_pwrdm(voltdm, pwrdm);
spin_lock_init(&pwrdm->_lock);
list_add(&pwrdm->node, &pwrdm_list); list_add(&pwrdm->node, &pwrdm_list);
...@@ -112,7 +124,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm) ...@@ -112,7 +124,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
for (i = 0; i < pwrdm->banks; i++) for (i = 0; i < pwrdm->banks; i++)
pwrdm->ret_mem_off_counter[i] = 0; pwrdm->ret_mem_off_counter[i] = 0;
pwrdm_wait_transition(pwrdm); arch_pwrdm->pwrdm_wait_transition(pwrdm);
pwrdm->state = pwrdm_read_pwrst(pwrdm); pwrdm->state = pwrdm_read_pwrst(pwrdm);
pwrdm->state_counter[pwrdm->state] = 1; pwrdm->state_counter[pwrdm->state] = 1;
...@@ -143,7 +155,7 @@ static void _update_logic_membank_counters(struct powerdomain *pwrdm) ...@@ -143,7 +155,7 @@ static void _update_logic_membank_counters(struct powerdomain *pwrdm)
static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
{ {
int prev, state, trace_state = 0; int prev, next, state, trace_state = 0;
if (pwrdm == NULL) if (pwrdm == NULL)
return -EINVAL; return -EINVAL;
...@@ -164,9 +176,10 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) ...@@ -164,9 +176,10 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
* If the power domain did not hit the desired state, * If the power domain did not hit the desired state,
* generate a trace event with both the desired and hit states * generate a trace event with both the desired and hit states
*/ */
if (state != prev) { next = pwrdm_read_next_pwrst(pwrdm);
if (next != prev) {
trace_state = (PWRDM_TRACE_STATES_FLAG | trace_state = (PWRDM_TRACE_STATES_FLAG |
((state & OMAP_POWERSTATE_MASK) << 8) | ((next & OMAP_POWERSTATE_MASK) << 8) |
((prev & OMAP_POWERSTATE_MASK) << 0)); ((prev & OMAP_POWERSTATE_MASK) << 0));
trace_power_domain_target(pwrdm->name, trace_state, trace_power_domain_target(pwrdm->name, trace_state,
smp_processor_id()); smp_processor_id());
...@@ -199,6 +212,80 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) ...@@ -199,6 +212,80 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
return 0; return 0;
} }
/**
* _pwrdm_save_clkdm_state_and_activate - prepare for power state change
* @pwrdm: struct powerdomain * to operate on
* @curr_pwrst: current power state of @pwrdm
* @pwrst: power state to switch to
* @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
*
* Determine whether the powerdomain needs to be turned on before
* attempting to switch power states. Called by
* omap_set_pwrdm_state(). NOTE that if the powerdomain contains
* multiple clockdomains, this code assumes that the first clockdomain
* supports software-supervised wakeup mode - potentially a problem.
* Returns the power state switch mode currently in use (see the
* "Types of sleep_switch" comment above).
*/
static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
u8 curr_pwrst, u8 pwrst,
bool *hwsup)
{
u8 sleep_switch;
if (curr_pwrst < 0) {
WARN_ON(1);
sleep_switch = ERROR_SWITCH;
} else if (curr_pwrst < PWRDM_POWER_ON) {
if (curr_pwrst > pwrst &&
pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
arch_pwrdm->pwrdm_set_lowpwrstchange) {
sleep_switch = LOWPOWERSTATE_SWITCH;
} else {
*hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);
sleep_switch = FORCEWAKEUP_SWITCH;
}
} else {
sleep_switch = ALREADYACTIVE_SWITCH;
}
return sleep_switch;
}
/**
* _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
* @pwrdm: struct powerdomain * to operate on
* @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
* @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
*
* Restore the clockdomain state perturbed by
* _pwrdm_save_clkdm_state_and_activate(), and call the power state
* bookkeeping code. Called by omap_set_pwrdm_state(). NOTE that if
* the powerdomain contains multiple clockdomains, this assumes that
* the first associated clockdomain supports either
* hardware-supervised idle control in the register, or
* software-supervised sleep. No return value.
*/
static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
u8 sleep_switch, bool hwsup)
{
switch (sleep_switch) {
case FORCEWAKEUP_SWITCH:
if (hwsup)
clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
else
clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);
break;
case LOWPOWERSTATE_SWITCH:
if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
arch_pwrdm->pwrdm_set_lowpwrstchange)
arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
pwrdm_state_switch_nolock(pwrdm);
break;
}
}
/* Public functions */ /* Public functions */
/** /**
...@@ -274,6 +361,30 @@ int pwrdm_complete_init(void) ...@@ -274,6 +361,30 @@ int pwrdm_complete_init(void)
return 0; return 0;
} }
/**
* pwrdm_lock - acquire a Linux spinlock on a powerdomain
* @pwrdm: struct powerdomain * to lock
*
* Acquire the powerdomain spinlock on @pwrdm. No return value.
*/
void pwrdm_lock(struct powerdomain *pwrdm)
__acquires(&pwrdm->_lock)
{
spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags);
}
/**
* pwrdm_unlock - release a Linux spinlock on a powerdomain
* @pwrdm: struct powerdomain * to unlock
*
* Release the powerdomain spinlock on @pwrdm. No return value.
*/
void pwrdm_unlock(struct powerdomain *pwrdm)
__releases(&pwrdm->_lock)
{
spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags);
}
/** /**
* pwrdm_lookup - look up a powerdomain by name, return a pointer * pwrdm_lookup - look up a powerdomain by name, return a pointer
* @name: name of powerdomain * @name: name of powerdomain
...@@ -920,65 +1031,27 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) ...@@ -920,65 +1031,27 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
} }
/** int pwrdm_state_switch_nolock(struct powerdomain *pwrdm)
* pwrdm_set_lowpwrstchange - Request a low power state change
* @pwrdm: struct powerdomain *
*
* Allows a powerdomain to transtion to a lower power sleep state
* from an existing sleep state without waking up the powerdomain.
* Returns -EINVAL if the powerdomain pointer is null or if the
* powerdomain does not support LOWPOWERSTATECHANGE, or returns 0
* upon success.
*/
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm)
{
int ret = -EINVAL;
if (!pwrdm)
return -EINVAL;
if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
return -EINVAL;
pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
pwrdm->name);
if (arch_pwrdm && arch_pwrdm->pwrdm_set_lowpwrstchange)
ret = arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
return ret;
}
/**
* pwrdm_wait_transition - wait for powerdomain power transition to finish
* @pwrdm: struct powerdomain * to wait for
*
* If the powerdomain @pwrdm is in the process of a state transition,
* spin until it completes the power transition, or until an iteration
* bailout value is reached. Returns -EINVAL if the powerdomain
* pointer is null, -EAGAIN if the bailout value was reached, or
* returns 0 upon success.
*/
int pwrdm_wait_transition(struct powerdomain *pwrdm)
{ {
int ret = -EINVAL; int ret;
if (!pwrdm) if (!pwrdm || !arch_pwrdm)
return -EINVAL; return -EINVAL;
if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition) ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
ret = arch_pwrdm->pwrdm_wait_transition(pwrdm); if (!ret)
ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
return ret; return ret;
} }
int pwrdm_state_switch(struct powerdomain *pwrdm) int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm)
{ {
int ret; int ret;
ret = pwrdm_wait_transition(pwrdm); pwrdm_lock(pwrdm);
if (!ret) ret = pwrdm_state_switch_nolock(pwrdm);
ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); pwrdm_unlock(pwrdm);
return ret; return ret;
} }
...@@ -1003,6 +1076,61 @@ int pwrdm_post_transition(struct powerdomain *pwrdm) ...@@ -1003,6 +1076,61 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
return 0; return 0;
} }
/**
* omap_set_pwrdm_state - change a powerdomain's current power state
* @pwrdm: struct powerdomain * to change the power state of
* @pwrst: power state to change to
*
* Change the current hardware power state of the powerdomain
* represented by @pwrdm to the power state represented by @pwrst.
* Returns -EINVAL if @pwrdm is null or invalid or if the
* powerdomain's current power state could not be read, or returns 0
* upon success or if @pwrdm does not support @pwrst or any
* lower-power state. XXX Should not return 0 if the @pwrdm does not
* support @pwrst or any lower-power state: this should be an error.
*/
int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
{
u8 curr_pwrst, next_pwrst, sleep_switch;
int ret = 0;
bool hwsup = false;
if (!pwrdm || IS_ERR(pwrdm))
return -EINVAL;
while (!(pwrdm->pwrsts & (1 << pwrst))) {
if (pwrst == PWRDM_POWER_OFF)
return ret;
pwrst--;
}
pwrdm_lock(pwrdm);
curr_pwrst = pwrdm_read_pwrst(pwrdm);
next_pwrst = pwrdm_read_next_pwrst(pwrdm);
if (curr_pwrst == pwrst && next_pwrst == pwrst)
goto osps_out;
sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
pwrst, &hwsup);
if (sleep_switch == ERROR_SWITCH) {
ret = -EINVAL;
goto osps_out;
}
ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
if (ret)
pr_err("%s: unable to set power state of powerdomain: %s\n",
__func__, pwrdm->name);
_pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
osps_out:
pwrdm_unlock(pwrdm);
return ret;
}
/** /**
* pwrdm_get_context_loss_count - get powerdomain's context loss count * pwrdm_get_context_loss_count - get powerdomain's context loss count
* @pwrdm: struct powerdomain * to wait for * @pwrdm: struct powerdomain * to wait for
......
...@@ -19,8 +19,7 @@ ...@@ -19,8 +19,7 @@
#include <linux/types.h> #include <linux/types.h>
#include <linux/list.h> #include <linux/list.h>
#include <linux/spinlock.h>
#include <linux/atomic.h>
#include "voltage.h" #include "voltage.h"
...@@ -44,18 +43,20 @@ ...@@ -44,18 +43,20 @@
#define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON) #define PWRSTS_OFF_RET_ON (PWRSTS_OFF_RET | PWRSTS_ON)
/* Powerdomain flags */ /*
#define PWRDM_HAS_HDWR_SAR (1 << 0) /* hardware save-and-restore support */ * Powerdomain flags (struct powerdomain.flags)
#define PWRDM_HAS_MPU_QUIRK (1 << 1) /* MPU pwr domain has MEM bank 0 bits *
* in MEM bank 1 position. This is * PWRDM_HAS_HDWR_SAR - powerdomain has hardware save-and-restore support
* true for OMAP3430 *
*/ * PWRDM_HAS_MPU_QUIRK - MPU pwr domain has MEM bank 0 bits in MEM
#define PWRDM_HAS_LOWPOWERSTATECHANGE (1 << 2) /* * bank 1 position. This is true for OMAP3430
* support to transition from a *
* sleep state to a lower sleep * PWRDM_HAS_LOWPOWERSTATECHANGE - can transition from a sleep state
* state without waking up the * to a lower sleep state without waking up the powerdomain
* powerdomain */
*/ #define PWRDM_HAS_HDWR_SAR BIT(0)
#define PWRDM_HAS_MPU_QUIRK BIT(1)
#define PWRDM_HAS_LOWPOWERSTATECHANGE BIT(2)
/* /*
* Number of memory banks that are power-controllable. On OMAP4430, the * Number of memory banks that are power-controllable. On OMAP4430, the
...@@ -103,6 +104,8 @@ struct powerdomain; ...@@ -103,6 +104,8 @@ struct powerdomain;
* @state_counter: * @state_counter:
* @timer: * @timer:
* @state_timer: * @state_timer:
* @_lock: spinlock used to serialize powerdomain and some clockdomain ops
* @_lock_flags: stored flags when @_lock is taken
* *
* @prcm_partition possible values are defined in mach-omap2/prcm44xx.h. * @prcm_partition possible values are defined in mach-omap2/prcm44xx.h.
*/ */
...@@ -127,7 +130,8 @@ struct powerdomain { ...@@ -127,7 +130,8 @@ struct powerdomain {
unsigned state_counter[PWRDM_MAX_PWRSTS]; unsigned state_counter[PWRDM_MAX_PWRSTS];
unsigned ret_logic_off_counter; unsigned ret_logic_off_counter;
unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS]; unsigned ret_mem_off_counter[PWRDM_MAX_MEM_BANKS];
spinlock_t _lock;
unsigned long _lock_flags;
const u8 pwrstctrl_offs; const u8 pwrstctrl_offs;
const u8 pwrstst_offs; const u8 pwrstst_offs;
const u32 logicretstate_mask; const u32 logicretstate_mask;
...@@ -162,6 +166,16 @@ struct powerdomain { ...@@ -162,6 +166,16 @@ struct powerdomain {
* @pwrdm_disable_hdwr_sar: Disable Hardware Save-Restore feature for a pd * @pwrdm_disable_hdwr_sar: Disable Hardware Save-Restore feature for a pd
* @pwrdm_set_lowpwrstchange: Enable pd transitions from a shallow to deep sleep * @pwrdm_set_lowpwrstchange: Enable pd transitions from a shallow to deep sleep
* @pwrdm_wait_transition: Wait for a pd state transition to complete * @pwrdm_wait_transition: Wait for a pd state transition to complete
*
* Regarding @pwrdm_set_lowpwrstchange: On the OMAP2 and 3-family
* chips, a powerdomain's power state is not allowed to directly
* transition from one low-power state (e.g., CSWR) to another
* low-power state (e.g., OFF) without first waking up the
* powerdomain. This wastes energy. So OMAP4 chips support the
* ability to transition a powerdomain power state directly from one
* low-power state to another. The function pointed to by
* @pwrdm_set_lowpwrstchange is intended to configure the OMAP4
* hardware powerdomain state machine to enable this feature.
*/ */
struct pwrdm_ops { struct pwrdm_ops {
int (*pwrdm_set_next_pwrst)(struct powerdomain *pwrdm, u8 pwrst); int (*pwrdm_set_next_pwrst)(struct powerdomain *pwrdm, u8 pwrst);
...@@ -225,15 +239,15 @@ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm); ...@@ -225,15 +239,15 @@ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm);
int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm); int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm);
bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm); bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);
int pwrdm_wait_transition(struct powerdomain *pwrdm); int pwrdm_state_switch_nolock(struct powerdomain *pwrdm);
int pwrdm_state_switch(struct powerdomain *pwrdm); int pwrdm_state_switch(struct powerdomain *pwrdm);
int pwrdm_pre_transition(struct powerdomain *pwrdm); int pwrdm_pre_transition(struct powerdomain *pwrdm);
int pwrdm_post_transition(struct powerdomain *pwrdm); int pwrdm_post_transition(struct powerdomain *pwrdm);
int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
int pwrdm_get_context_loss_count(struct powerdomain *pwrdm); int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm); bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 state);
extern void omap242x_powerdomains_init(void); extern void omap242x_powerdomains_init(void);
extern void omap243x_powerdomains_init(void); extern void omap243x_powerdomains_init(void);
extern void omap3xxx_powerdomains_init(void); extern void omap3xxx_powerdomains_init(void);
...@@ -253,5 +267,7 @@ extern u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank); ...@@ -253,5 +267,7 @@ extern u32 omap2_pwrdm_get_mem_bank_stst_mask(u8 bank);
extern struct powerdomain wkup_omap2_pwrdm; extern struct powerdomain wkup_omap2_pwrdm;
extern struct powerdomain gfx_omap2_pwrdm; extern struct powerdomain gfx_omap2_pwrdm;
extern void pwrdm_lock(struct powerdomain *pwrdm);
extern void pwrdm_unlock(struct powerdomain *pwrdm);
#endif #endif
...@@ -54,12 +54,12 @@ struct powerdomain gfx_omap2_pwrdm = { ...@@ -54,12 +54,12 @@ struct powerdomain gfx_omap2_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
struct powerdomain wkup_omap2_pwrdm = { struct powerdomain wkup_omap2_pwrdm = {
.name = "wkup_pwrdm", .name = "wkup_pwrdm",
.prcm_offs = WKUP_MOD, .prcm_offs = WKUP_MOD,
.pwrsts = PWRSTS_ON, .pwrsts = PWRSTS_ON,
.voltdm = { .name = "wakeup" }, .voltdm = { .name = "wakeup" },
}; };
...@@ -38,7 +38,7 @@ static struct powerdomain dsp_pwrdm = { ...@@ -38,7 +38,7 @@ static struct powerdomain dsp_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, [0] = PWRSTS_ON,
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain mpu_24xx_pwrdm = { static struct powerdomain mpu_24xx_pwrdm = {
...@@ -53,13 +53,14 @@ static struct powerdomain mpu_24xx_pwrdm = { ...@@ -53,13 +53,14 @@ static struct powerdomain mpu_24xx_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, [0] = PWRSTS_ON,
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain core_24xx_pwrdm = { static struct powerdomain core_24xx_pwrdm = {
.name = "core_pwrdm", .name = "core_pwrdm",
.prcm_offs = CORE_MOD, .prcm_offs = CORE_MOD,
.pwrsts = PWRSTS_OFF_RET_ON, .pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET,
.banks = 3, .banks = 3,
.pwrsts_mem_ret = { .pwrsts_mem_ret = {
[0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */ [0] = PWRSTS_OFF_RET, /* MEM1RETSTATE */
...@@ -71,7 +72,7 @@ static struct powerdomain core_24xx_pwrdm = { ...@@ -71,7 +72,7 @@ static struct powerdomain core_24xx_pwrdm = {
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
[2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */ [2] = PWRSTS_OFF_RET_ON, /* MEM3ONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
...@@ -93,7 +94,7 @@ static struct powerdomain mdm_pwrdm = { ...@@ -93,7 +94,7 @@ static struct powerdomain mdm_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
/* /*
......
...@@ -50,7 +50,7 @@ static struct powerdomain iva2_pwrdm = { ...@@ -50,7 +50,7 @@ static struct powerdomain iva2_pwrdm = {
[2] = PWRSTS_OFF_ON, [2] = PWRSTS_OFF_ON,
[3] = PWRSTS_ON, [3] = PWRSTS_ON,
}, },
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain mpu_3xxx_pwrdm = { static struct powerdomain mpu_3xxx_pwrdm = {
...@@ -66,7 +66,7 @@ static struct powerdomain mpu_3xxx_pwrdm = { ...@@ -66,7 +66,7 @@ static struct powerdomain mpu_3xxx_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_OFF_ON, [0] = PWRSTS_OFF_ON,
}, },
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain mpu_am35x_pwrdm = { static struct powerdomain mpu_am35x_pwrdm = {
...@@ -82,7 +82,7 @@ static struct powerdomain mpu_am35x_pwrdm = { ...@@ -82,7 +82,7 @@ static struct powerdomain mpu_am35x_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, [0] = PWRSTS_ON,
}, },
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
/* /*
...@@ -109,7 +109,7 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = { ...@@ -109,7 +109,7 @@ static struct powerdomain core_3xxx_pre_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */ [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain core_3xxx_es3_1_pwrdm = { static struct powerdomain core_3xxx_es3_1_pwrdm = {
...@@ -131,7 +131,7 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = { ...@@ -131,7 +131,7 @@ static struct powerdomain core_3xxx_es3_1_pwrdm = {
[0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */ [0] = PWRSTS_OFF_RET_ON, /* MEM1ONSTATE */
[1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */ [1] = PWRSTS_OFF_RET_ON, /* MEM2ONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain core_am35x_pwrdm = { static struct powerdomain core_am35x_pwrdm = {
...@@ -148,7 +148,7 @@ static struct powerdomain core_am35x_pwrdm = { ...@@ -148,7 +148,7 @@ static struct powerdomain core_am35x_pwrdm = {
[0] = PWRSTS_ON, /* MEM1ONSTATE */ [0] = PWRSTS_ON, /* MEM1ONSTATE */
[1] = PWRSTS_ON, /* MEM2ONSTATE */ [1] = PWRSTS_ON, /* MEM2ONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain dss_pwrdm = { static struct powerdomain dss_pwrdm = {
...@@ -163,7 +163,7 @@ static struct powerdomain dss_pwrdm = { ...@@ -163,7 +163,7 @@ static struct powerdomain dss_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain dss_am35x_pwrdm = { static struct powerdomain dss_am35x_pwrdm = {
...@@ -178,7 +178,7 @@ static struct powerdomain dss_am35x_pwrdm = { ...@@ -178,7 +178,7 @@ static struct powerdomain dss_am35x_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
/* /*
...@@ -199,7 +199,7 @@ static struct powerdomain sgx_pwrdm = { ...@@ -199,7 +199,7 @@ static struct powerdomain sgx_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain sgx_am35x_pwrdm = { static struct powerdomain sgx_am35x_pwrdm = {
...@@ -214,7 +214,7 @@ static struct powerdomain sgx_am35x_pwrdm = { ...@@ -214,7 +214,7 @@ static struct powerdomain sgx_am35x_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain cam_pwrdm = { static struct powerdomain cam_pwrdm = {
...@@ -229,7 +229,7 @@ static struct powerdomain cam_pwrdm = { ...@@ -229,7 +229,7 @@ static struct powerdomain cam_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain per_pwrdm = { static struct powerdomain per_pwrdm = {
...@@ -244,7 +244,7 @@ static struct powerdomain per_pwrdm = { ...@@ -244,7 +244,7 @@ static struct powerdomain per_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain per_am35x_pwrdm = { static struct powerdomain per_am35x_pwrdm = {
...@@ -259,13 +259,13 @@ static struct powerdomain per_am35x_pwrdm = { ...@@ -259,13 +259,13 @@ static struct powerdomain per_am35x_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain emu_pwrdm = { static struct powerdomain emu_pwrdm = {
.name = "emu_pwrdm", .name = "emu_pwrdm",
.prcm_offs = OMAP3430_EMU_MOD, .prcm_offs = OMAP3430_EMU_MOD,
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain neon_pwrdm = { static struct powerdomain neon_pwrdm = {
...@@ -273,7 +273,7 @@ static struct powerdomain neon_pwrdm = { ...@@ -273,7 +273,7 @@ static struct powerdomain neon_pwrdm = {
.prcm_offs = OMAP3430_NEON_MOD, .prcm_offs = OMAP3430_NEON_MOD,
.pwrsts = PWRSTS_OFF_RET_ON, .pwrsts = PWRSTS_OFF_RET_ON,
.pwrsts_logic_ret = PWRSTS_RET, .pwrsts_logic_ret = PWRSTS_RET,
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain neon_am35x_pwrdm = { static struct powerdomain neon_am35x_pwrdm = {
...@@ -281,7 +281,7 @@ static struct powerdomain neon_am35x_pwrdm = { ...@@ -281,7 +281,7 @@ static struct powerdomain neon_am35x_pwrdm = {
.prcm_offs = OMAP3430_NEON_MOD, .prcm_offs = OMAP3430_NEON_MOD,
.pwrsts = PWRSTS_ON, .pwrsts = PWRSTS_ON,
.pwrsts_logic_ret = PWRSTS_ON, .pwrsts_logic_ret = PWRSTS_ON,
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain usbhost_pwrdm = { static struct powerdomain usbhost_pwrdm = {
...@@ -303,37 +303,37 @@ static struct powerdomain usbhost_pwrdm = { ...@@ -303,37 +303,37 @@ static struct powerdomain usbhost_pwrdm = {
.pwrsts_mem_on = { .pwrsts_mem_on = {
[0] = PWRSTS_ON, /* MEMONSTATE */ [0] = PWRSTS_ON, /* MEMONSTATE */
}, },
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain dpll1_pwrdm = { static struct powerdomain dpll1_pwrdm = {
.name = "dpll1_pwrdm", .name = "dpll1_pwrdm",
.prcm_offs = MPU_MOD, .prcm_offs = MPU_MOD,
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain dpll2_pwrdm = { static struct powerdomain dpll2_pwrdm = {
.name = "dpll2_pwrdm", .name = "dpll2_pwrdm",
.prcm_offs = OMAP3430_IVA2_MOD, .prcm_offs = OMAP3430_IVA2_MOD,
.voltdm = { .name = "mpu_iva" }, .voltdm = { .name = "mpu_iva" },
}; };
static struct powerdomain dpll3_pwrdm = { static struct powerdomain dpll3_pwrdm = {
.name = "dpll3_pwrdm", .name = "dpll3_pwrdm",
.prcm_offs = PLL_MOD, .prcm_offs = PLL_MOD,
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain dpll4_pwrdm = { static struct powerdomain dpll4_pwrdm = {
.name = "dpll4_pwrdm", .name = "dpll4_pwrdm",
.prcm_offs = PLL_MOD, .prcm_offs = PLL_MOD,
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
static struct powerdomain dpll5_pwrdm = { static struct powerdomain dpll5_pwrdm = {
.name = "dpll5_pwrdm", .name = "dpll5_pwrdm",
.prcm_offs = PLL_MOD, .prcm_offs = PLL_MOD,
.voltdm = { .name = "core" }, .voltdm = { .name = "core" },
}; };
/* As powerdomains are added or removed above, this list must also be changed */ /* As powerdomains are added or removed above, this list must also be changed */
......
...@@ -210,6 +210,7 @@ int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1, ...@@ -210,6 +210,7 @@ int omap2_clkdm_read_wkdep(struct clockdomain *clkdm1,
PM_WKDEP, (1 << clkdm2->dep_bit)); PM_WKDEP, (1 << clkdm2->dep_bit));
} }
/* XXX Caller must hold the clkdm's powerdomain lock */
int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm) int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
{ {
struct clkdm_dep *cd; struct clkdm_dep *cd;
...@@ -221,7 +222,7 @@ int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm) ...@@ -221,7 +222,7 @@ int omap2_clkdm_clear_all_wkdeps(struct clockdomain *clkdm)
/* PRM accesses are slow, so minimize them */ /* PRM accesses are slow, so minimize them */
mask |= 1 << cd->clkdm->dep_bit; mask |= 1 << cd->clkdm->dep_bit;
atomic_set(&cd->wkdep_usecount, 0); cd->wkdep_usecount = 0;
} }
omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs, omap2_prm_clear_mod_reg_bits(mask, clkdm->pwrdm.ptr->prcm_offs,
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment