diff options
Diffstat (limited to 'arch/arm/mach-omap2/powerdomain.c')
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 114 |
1 files changed, 108 insertions, 6 deletions
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 983f1cb676be..2594cbff3947 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -35,6 +35,13 @@ #include <mach/powerdomain.h> #include <mach/clockdomain.h> +#include "pm.h" + +enum { + PWRDM_STATE_NOW = 0, + PWRDM_STATE_PREV, +}; + /* pwrdm_list contains all registered struct powerdomains */ static LIST_HEAD(pwrdm_list); @@ -83,7 +90,7 @@ static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm, if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip)) return ERR_PTR(-EINVAL); - for (pd = deps; pd; pd++) { + for (pd = deps; pd->pwrdm_name; pd++) { if (!omap_chip_is(pd->omap_chip)) continue; @@ -96,12 +103,71 @@ static struct powerdomain *_pwrdm_deps_lookup(struct powerdomain *pwrdm, } - if (!pd) + if (!pd->pwrdm_name) return ERR_PTR(-ENOENT); return pd->pwrdm; } +static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) +{ + + int prev; + int state; + + if (pwrdm == NULL) + return -EINVAL; + + state = pwrdm_read_pwrst(pwrdm); + + switch (flag) { + case PWRDM_STATE_NOW: + prev = pwrdm->state; + break; + case PWRDM_STATE_PREV: + prev = pwrdm_read_prev_pwrst(pwrdm); + if (pwrdm->state != prev) + pwrdm->state_counter[prev]++; + break; + default: + return -EINVAL; + } + + if (state != prev) + pwrdm->state_counter[state]++; + + pm_dbg_update_time(pwrdm, prev); + + pwrdm->state = state; + + return 0; +} + +static int _pwrdm_pre_transition_cb(struct powerdomain *pwrdm, void *unused) +{ + pwrdm_clear_all_prev_pwrst(pwrdm); + _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + return 0; +} + +static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) +{ + _pwrdm_state_switch(pwrdm, PWRDM_STATE_PREV); + return 0; +} + +static __init void _pwrdm_setup(struct powerdomain *pwrdm) +{ + int i; + + for (i = 0; i < 4; i++) + pwrdm->state_counter[i] = 0; + + pwrdm_wait_transition(pwrdm); + pwrdm->state = pwrdm_read_pwrst(pwrdm); + pwrdm->state_counter[pwrdm->state] = 1; + +} /* Public functions */ @@ -117,9 +183,12 @@ void pwrdm_init(struct powerdomain **pwrdm_list) { struct powerdomain **p = NULL; - if (pwrdm_list) - for (p = pwrdm_list; *p; p++) + if (pwrdm_list) { + for (p = pwrdm_list; *p; p++) { pwrdm_register(*p); + _pwrdm_setup(*p); + } + } } /** @@ -217,7 +286,8 @@ struct powerdomain *pwrdm_lookup(const char *name) * anything else to indicate failure; or -EINVAL if the function * pointer is null. */ -int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm)) +int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user) { struct powerdomain *temp_pwrdm; unsigned long flags; @@ -228,7 +298,7 @@ int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm)) read_lock_irqsave(&pwrdm_rwlock, flags); list_for_each_entry(temp_pwrdm, &pwrdm_list, node) { - ret = (*fn)(temp_pwrdm); + ret = (*fn)(temp_pwrdm, user); if (ret) break; } @@ -1110,4 +1180,36 @@ int pwrdm_wait_transition(struct powerdomain *pwrdm) return 0; } +int pwrdm_state_switch(struct powerdomain *pwrdm) +{ + return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); +} + +int pwrdm_clkdm_state_switch(struct clockdomain *clkdm) +{ + if (clkdm != NULL && clkdm->pwrdm.ptr != NULL) { + pwrdm_wait_transition(clkdm->pwrdm.ptr); + return pwrdm_state_switch(clkdm->pwrdm.ptr); + } + + return -EINVAL; +} +int pwrdm_clk_state_switch(struct clk *clk) +{ + if (clk != NULL && clk->clkdm != NULL) + return pwrdm_clkdm_state_switch(clk->clkdm); + return -EINVAL; +} + +int pwrdm_pre_transition(void) +{ + pwrdm_for_each(_pwrdm_pre_transition_cb, NULL); + return 0; +} + +int pwrdm_post_transition(void) +{ + pwrdm_for_each(_pwrdm_post_transition_cb, NULL); + return 0; +} |