diff options
Diffstat (limited to 'arch/arm/mach-omap2/powerdomain.c')
| -rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 1297 |
1 files changed, 638 insertions, 659 deletions
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c index 26b3f3ee82a..faebd5f076a 100644 --- a/arch/arm/mach-omap2/powerdomain.c +++ b/arch/arm/mach-omap2/powerdomain.c @@ -1,10 +1,12 @@ /* * OMAP powerdomain control * - * Copyright (C) 2007-2008 Texas Instruments, Inc. - * Copyright (C) 2007-2008 Nokia Corporation + * Copyright (C) 2007-2008, 2011 Texas Instruments, Inc. + * Copyright (C) 2007-2011 Nokia Corporation * * Written by Paul Walmsley + * Added OMAP4 specific support by Abhijit Pagare <abhijitpagare@ti.com> + * State counting code by Tero Kristo <tero.kristo@nokia.com> * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 as @@ -13,55 +15,51 @@ #undef DEBUG #include <linux/kernel.h> -#include <linux/module.h> #include <linux/types.h> -#include <linux/delay.h> -#include <linux/spinlock.h> #include <linux/list.h> #include <linux/errno.h> -#include <linux/err.h> -#include <linux/io.h> +#include <linux/string.h> +#include <linux/spinlock.h> +#include <trace/events/power.h> -#include <asm/atomic.h> +#include "cm2xxx_3xxx.h" +#include "prcm44xx.h" +#include "cm44xx.h" +#include "prm2xxx_3xxx.h" +#include "prm44xx.h" -#include "cm.h" -#include "cm-regbits-34xx.h" -#include "prm.h" -#include "prm-regbits-34xx.h" +#include <asm/cpu.h> -#include <plat/cpu.h> -#include <plat/powerdomain.h> -#include <plat/clockdomain.h> +#include "powerdomain.h" +#include "clockdomain.h" +#include "voltage.h" +#include "soc.h" #include "pm.h" +#define PWRDM_TRACE_STATES_FLAG (1<<31) + enum { PWRDM_STATE_NOW = 0, PWRDM_STATE_PREV, }; -/* pwrdm_list contains all registered struct powerdomains */ -static LIST_HEAD(pwrdm_list); - /* - * pwrdm_rwlock protects pwrdm_list add and del ops - also reused to - * protect pwrdm_clkdms[] during clkdm add/del ops + * Types of sleep_switch used internally in omap_set_pwrdm_state() + * and its associated static functions + * + * XXX Better documentation is needed here */ -static DEFINE_RWLOCK(pwrdm_rwlock); +#define ALREADYACTIVE_SWITCH 0 +#define FORCEWAKEUP_SWITCH 1 +#define LOWPOWERSTATE_SWITCH 2 +/* pwrdm_list contains all registered struct powerdomains */ +static LIST_HEAD(pwrdm_list); -/* 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); +static struct pwrdm_ops *arch_pwrdm; - return v; -} +/* Private functions */ static struct powerdomain *_pwrdm_lookup(const char *name) { @@ -79,39 +77,91 @@ 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) +/** + * _pwrdm_register - register a powerdomain + * @pwrdm: struct powerdomain * to register + * + * Adds a powerdomain to the internal powerdomain list. Returns + * -EINVAL if given a null pointer, -EEXIST if a powerdomain is + * already registered by the provided name, or 0 upon success. + */ +static int _pwrdm_register(struct powerdomain *pwrdm) { - struct pwrdm_dep *pd; - - if (!pwrdm || !deps || !omap_chip_is(pwrdm->omap_chip)) - return ERR_PTR(-EINVAL); + int i; + struct voltagedomain *voltdm; - for (pd = deps; pd->pwrdm_name; pd++) { + if (!pwrdm || !pwrdm->name) + return -EINVAL; - if (!omap_chip_is(pd->omap_chip)) - continue; + if (cpu_is_omap44xx() && + pwrdm->prcm_partition == OMAP4430_INVALID_PRCM_PARTITION) { + pr_err("powerdomain: %s: missing OMAP4 PRCM partition ID\n", + pwrdm->name); + return -EINVAL; + } - if (!pd->pwrdm && pd->pwrdm_name) - pd->pwrdm = pwrdm_lookup(pd->pwrdm_name); + if (_pwrdm_lookup(pwrdm->name)) + return -EEXIST; - if (pd->pwrdm == pwrdm) - break; + if (arch_pwrdm && arch_pwrdm->pwrdm_has_voltdm) + if (!arch_pwrdm->pwrdm_has_voltdm()) + goto skip_voltdm; + voltdm = voltdm_lookup(pwrdm->voltdm.name); + if (!voltdm) { + pr_err("powerdomain: %s: voltagedomain %s does not exist\n", + pwrdm->name, pwrdm->voltdm.name); + return -EINVAL; } + pwrdm->voltdm.ptr = voltdm; + INIT_LIST_HEAD(&pwrdm->voltdm_node); + voltdm_add_pwrdm(voltdm, pwrdm); +skip_voltdm: + spin_lock_init(&pwrdm->_lock); + + list_add(&pwrdm->node, &pwrdm_list); + + /* Initialize the powerdomain's state counter */ + for (i = 0; i < PWRDM_MAX_PWRSTS; i++) + pwrdm->state_counter[i] = 0; + + pwrdm->ret_logic_off_counter = 0; + for (i = 0; i < pwrdm->banks; i++) + pwrdm->ret_mem_off_counter[i] = 0; - if (!pd->pwrdm_name) - return ERR_PTR(-ENOENT); + if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition) + arch_pwrdm->pwrdm_wait_transition(pwrdm); + pwrdm->state = pwrdm_read_pwrst(pwrdm); + pwrdm->state_counter[pwrdm->state] = 1; - return pd->pwrdm; + pr_debug("powerdomain: registered %s\n", pwrdm->name); + + return 0; +} + +static void _update_logic_membank_counters(struct powerdomain *pwrdm) +{ + int i; + u8 prev_logic_pwrst, prev_mem_pwrst; + + prev_logic_pwrst = pwrdm_read_prev_logic_pwrst(pwrdm); + if ((pwrdm->pwrsts_logic_ret == PWRSTS_OFF_RET) && + (prev_logic_pwrst == PWRDM_POWER_OFF)) + pwrdm->ret_logic_off_counter++; + + for (i = 0; i < pwrdm->banks; i++) { + prev_mem_pwrst = pwrdm_read_prev_mem_pwrst(pwrdm, i); + + if ((pwrdm->pwrsts_mem_ret[i] == PWRSTS_OFF_RET) && + (prev_mem_pwrst == PWRDM_POWER_OFF)) + pwrdm->ret_mem_off_counter[i]++; + } } static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) { - int prev; - int state; + int prev, next, state, trace_state = 0; if (pwrdm == NULL) return -EINVAL; @@ -126,6 +176,20 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) prev = pwrdm_read_prev_pwrst(pwrdm); if (pwrdm->state != prev) pwrdm->state_counter[prev]++; + if (prev == PWRDM_POWER_RET) + _update_logic_membank_counters(pwrdm); + /* + * If the power domain did not hit the desired state, + * generate a trace event with both the desired and hit states + */ + next = pwrdm_read_next_pwrst(pwrdm); + if (next != prev) { + trace_state = (PWRDM_TRACE_STATES_FLAG | + ((next & OMAP_POWERSTATE_MASK) << 8) | + ((prev & OMAP_POWERSTATE_MASK) << 0)); + trace_power_domain_target(pwrdm->name, trace_state, + smp_processor_id()); + } break; default: return -EINVAL; @@ -154,134 +218,207 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused) return 0; } -static __init void _pwrdm_setup(struct powerdomain *pwrdm) +/** + * _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) { - int i; - - for (i = 0; i < PWRDM_MAX_PWRSTS; i++) - pwrdm->state_counter[i] = 0; + u8 sleep_switch; + + 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; + } - pwrdm_wait_transition(pwrdm); - pwrdm->state = pwrdm_read_pwrst(pwrdm); - pwrdm->state_counter[pwrdm->state] = 1; + 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 */ /** - * pwrdm_init - set up the powerdomain layer + * pwrdm_register_platform_funcs - register powerdomain implementation fns + * @po: func pointers for arch specific implementations * - * Loop through the list of powerdomains, registering all that are - * available on the current CPU. If pwrdm_list is supplied and not - * null, all of the referenced powerdomains will be registered. No - * return value. + * Register the list of function pointers used to implement the + * powerdomain functions on different OMAP SoCs. Should be called + * before any other pwrdm_register*() function. Returns -EINVAL if + * @po is null, -EEXIST if platform functions have already been + * registered, or 0 upon success. */ -void pwrdm_init(struct powerdomain **pwrdm_list) +int pwrdm_register_platform_funcs(struct pwrdm_ops *po) { - struct powerdomain **p = NULL; + if (!po) + return -EINVAL; - if (pwrdm_list) { - for (p = pwrdm_list; *p; p++) { - pwrdm_register(*p); - _pwrdm_setup(*p); - } - } + if (arch_pwrdm) + return -EEXIST; + + arch_pwrdm = po; + + return 0; } /** - * pwrdm_register - register a powerdomain - * @pwrdm: struct powerdomain * to register + * pwrdm_register_pwrdms - register SoC powerdomains + * @ps: pointer to an array of struct powerdomain to register * - * Adds a powerdomain to the internal powerdomain list. Returns - * -EINVAL if given a null pointer, -EEXIST if a powerdomain is - * already registered by the provided name, or 0 upon success. + * Register the powerdomains available on a particular OMAP SoC. Must + * be called after pwrdm_register_platform_funcs(). May be called + * multiple times. Returns -EACCES if called before + * pwrdm_register_platform_funcs(); -EINVAL if the argument @ps is + * null; or 0 upon success. */ -int pwrdm_register(struct powerdomain *pwrdm) +int pwrdm_register_pwrdms(struct powerdomain **ps) { - unsigned long flags; - int ret = -EINVAL; + struct powerdomain **p = NULL; - if (!pwrdm) - return -EINVAL; + if (!arch_pwrdm) + return -EEXIST; - if (!omap_chip_is(pwrdm->omap_chip)) + if (!ps) return -EINVAL; - write_lock_irqsave(&pwrdm_rwlock, flags); - if (_pwrdm_lookup(pwrdm->name)) { - ret = -EEXIST; - goto pr_unlock; - } - - list_add(&pwrdm->node, &pwrdm_list); - - pr_debug("powerdomain: registered %s\n", pwrdm->name); - ret = 0; + for (p = ps; *p; p++) + _pwrdm_register(*p); -pr_unlock: - write_unlock_irqrestore(&pwrdm_rwlock, flags); - - return ret; + return 0; } /** - * pwrdm_unregister - unregister a powerdomain - * @pwrdm: struct powerdomain * to unregister + * pwrdm_complete_init - set up the powerdomain layer * - * Removes a powerdomain from the internal powerdomain list. Returns - * -EINVAL if pwrdm argument is NULL. + * Do whatever is necessary to initialize registered powerdomains and + * powerdomain code. Currently, this programs the next power state + * for each powerdomain to ON. This prevents powerdomains from + * unexpectedly losing context or entering high wakeup latency modes + * with non-power-management-enabled kernels. Must be called after + * pwrdm_register_pwrdms(). Returns -EACCES if called before + * pwrdm_register_pwrdms(), or 0 upon success. */ -int pwrdm_unregister(struct powerdomain *pwrdm) +int pwrdm_complete_init(void) { - unsigned long flags; + struct powerdomain *temp_p; - if (!pwrdm) - return -EINVAL; + if (list_empty(&pwrdm_list)) + return -EACCES; - write_lock_irqsave(&pwrdm_rwlock, flags); - list_del(&pwrdm->node); - write_unlock_irqrestore(&pwrdm_rwlock, flags); - - pr_debug("powerdomain: unregistered %s\n", pwrdm->name); + list_for_each_entry(temp_p, &pwrdm_list, node) + pwrdm_set_next_pwrst(temp_p, PWRDM_POWER_ON); 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 * @name: name of powerdomain * - * Find a registered powerdomain by its name. Returns a pointer to the - * struct powerdomain if found, or NULL otherwise. + * Find a registered powerdomain by its name @name. Returns a pointer + * to the struct powerdomain if found, or NULL otherwise. */ struct powerdomain *pwrdm_lookup(const char *name) { struct powerdomain *pwrdm; - unsigned long flags; if (!name) return NULL; - read_lock_irqsave(&pwrdm_rwlock, flags); pwrdm = _pwrdm_lookup(name); - read_unlock_irqrestore(&pwrdm_rwlock, flags); return pwrdm; } /** - * pwrdm_for_each_nolock - call function on each registered clockdomain + * pwrdm_for_each - call function on each registered clockdomain * @fn: callback function * * - * Call the supplied function for each registered powerdomain. The - * callback function can return anything but 0 to bail out early from - * the iterator. Returns the last return value of the callback function, which - * should be 0 for success or anything else to indicate failure; or -EINVAL if - * the function pointer is null. + * Call the supplied function @fn for each registered powerdomain. + * The callback function @fn can return anything but 0 to bail out + * early from the iterator. Returns the last return value of the + * callback function, which should be 0 for success or anything else + * to indicate failure; or -EINVAL if the function pointer is null. */ -int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user), - void *user) +int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), + void *user) { struct powerdomain *temp_pwrdm; int ret = 0; @@ -299,50 +436,25 @@ int pwrdm_for_each_nolock(int (*fn)(struct powerdomain *pwrdm, void *user), } /** - * pwrdm_for_each - call function on each registered clockdomain - * @fn: callback function * - * - * This function is the same as 'pwrdm_for_each_nolock()', but keeps the - * &pwrdm_rwlock locked for reading, so no powerdomain structure manipulation - * functions should be called from the callback, although hardware powerdomain - * control functions are fine. - */ -int pwrdm_for_each(int (*fn)(struct powerdomain *pwrdm, void *user), - void *user) -{ - unsigned long flags; - int ret; - - read_lock_irqsave(&pwrdm_rwlock, flags); - ret = pwrdm_for_each_nolock(fn, user); - read_unlock_irqrestore(&pwrdm_rwlock, flags); - - return ret; -} - -/** * pwrdm_add_clkdm - add a clockdomain to a powerdomain * @pwrdm: struct powerdomain * to add the clockdomain to * @clkdm: struct clockdomain * to associate with a powerdomain * - * Associate the clockdomain 'clkdm' with a powerdomain 'pwrdm'. This + * Associate the clockdomain @clkdm with a powerdomain @pwrdm. This * enables the use of pwrdm_for_each_clkdm(). Returns -EINVAL if * presented with invalid pointers; -ENOMEM if memory could not be allocated; * or 0 upon success. */ int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) { - unsigned long flags; int i; int ret = -EINVAL; if (!pwrdm || !clkdm) return -EINVAL; - pr_debug("powerdomain: associating clockdomain %s with powerdomain " - "%s\n", clkdm->name, pwrdm->name); - - write_lock_irqsave(&pwrdm_rwlock, flags); + pr_debug("powerdomain: %s: associating clockdomain %s\n", + pwrdm->name, clkdm->name); for (i = 0; i < PWRDM_MAX_CLKDMS; i++) { if (!pwrdm->pwrdm_clkdms[i]) @@ -356,8 +468,8 @@ int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) } if (i == PWRDM_MAX_CLKDMS) { - pr_debug("powerdomain: increase PWRDM_MAX_CLKDMS for " - "pwrdm %s clkdm %s\n", pwrdm->name, clkdm->name); + pr_debug("powerdomain: %s: increase PWRDM_MAX_CLKDMS for clkdm %s\n", + pwrdm->name, clkdm->name); WARN_ON(1); ret = -ENOMEM; goto pac_exit; @@ -368,8 +480,6 @@ int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) ret = 0; pac_exit: - write_unlock_irqrestore(&pwrdm_rwlock, flags); - return ret; } @@ -378,32 +488,29 @@ pac_exit: * @pwrdm: struct powerdomain * to add the clockdomain to * @clkdm: struct clockdomain * to associate with a powerdomain * - * Dissociate the clockdomain 'clkdm' from the powerdomain - * 'pwrdm'. Returns -EINVAL if presented with invalid pointers; - * -ENOENT if the clkdm was not associated with the powerdomain, or 0 - * upon success. + * Dissociate the clockdomain @clkdm from the powerdomain + * @pwrdm. Returns -EINVAL if presented with invalid pointers; -ENOENT + * if @clkdm was not associated with the powerdomain, or 0 upon + * success. */ int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) { - unsigned long flags; int ret = -EINVAL; int i; if (!pwrdm || !clkdm) return -EINVAL; - pr_debug("powerdomain: dissociating clockdomain %s from powerdomain " - "%s\n", clkdm->name, pwrdm->name); - - write_lock_irqsave(&pwrdm_rwlock, flags); + pr_debug("powerdomain: %s: dissociating clockdomain %s\n", + pwrdm->name, clkdm->name); for (i = 0; i < PWRDM_MAX_CLKDMS; i++) if (pwrdm->pwrdm_clkdms[i] == clkdm) break; if (i == PWRDM_MAX_CLKDMS) { - pr_debug("powerdomain: clkdm %s not associated with pwrdm " - "%s ?!\n", clkdm->name, pwrdm->name); + pr_debug("powerdomain: %s: clkdm %s not associated?!\n", + pwrdm->name, clkdm->name); ret = -ENOENT; goto pdc_exit; } @@ -413,8 +520,6 @@ int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm) ret = 0; pdc_exit: - write_unlock_irqrestore(&pwrdm_rwlock, flags); - return ret; } @@ -423,259 +528,46 @@ pdc_exit: * @pwrdm: struct powerdomain * to iterate over * @fn: callback function * * - * Call the supplied function for each clockdomain in the powerdomain - * 'pwrdm'. The callback function can return anything but 0 to bail - * out early from the iterator. The callback function is called with - * the pwrdm_rwlock held for reading, so no powerdomain structure - * manipulation functions should be called from the callback, although - * hardware powerdomain control functions are fine. Returns -EINVAL - * if presented with invalid pointers; or passes along the last return - * value of the callback function, which should be 0 for success or - * anything else to indicate failure. + * Call the supplied function @fn for each clockdomain in the powerdomain + * @pwrdm. The callback function can return anything but 0 to bail + * out early from the iterator. Returns -EINVAL if presented with + * invalid pointers; or passes along the last return value of the + * callback function, which should be 0 for success or anything else + * to indicate failure. */ int pwrdm_for_each_clkdm(struct powerdomain *pwrdm, int (*fn)(struct powerdomain *pwrdm, struct clockdomain *clkdm)) { - unsigned long flags; int ret = 0; int i; if (!fn) return -EINVAL; - read_lock_irqsave(&pwrdm_rwlock, flags); - for (i = 0; i < PWRDM_MAX_CLKDMS && !ret; i++) ret = (*fn)(pwrdm, pwrdm->pwrdm_clkdms[i]); - read_unlock_irqrestore(&pwrdm_rwlock, flags); - 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. + * pwrdm_get_voltdm - return a ptr to the voltdm that this pwrdm resides in + * @pwrdm: struct powerdomain * * - * REVISIT: Currently this function only represents software-controllable - * sleep dependencies. Sleep dependencies fixed in hardware are not - * yet handled here. + * Return a pointer to the struct voltageomain that the specified powerdomain + * @pwrdm exists in. */ -int pwrdm_read_sleepdep(struct powerdomain *pwrdm1, struct powerdomain *pwrdm2) +struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm) { - 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)); + return pwrdm->voltdm.ptr; } /** * pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain * @pwrdm: struct powerdomain * * - * Return the number of controllable memory banks in powerdomain pwrdm, + * Return the number of controllable memory banks in powerdomain @pwrdm, * starting with 1. Returns -EINVAL if the powerdomain pointer is null. */ int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) @@ -691,7 +583,7 @@ int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) * @pwrdm: struct powerdomain * to set * @pwrst: one of the PWRDM_POWER_* macros * - * Set the powerdomain pwrdm's next power state to pwrst. The powerdomain + * Set the powerdomain @pwrdm's next power state to @pwrst. The powerdomain * may not enter this state immediately if the preconditions for this state * have not been satisfied. Returns -EINVAL if the powerdomain pointer is * null or if the power state is invalid for the powerdomin, or returns 0 @@ -699,71 +591,93 @@ int pwrdm_get_mem_bank_count(struct powerdomain *pwrdm) */ int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; if (!(pwrdm->pwrsts & (1 << pwrst))) return -EINVAL; - pr_debug("powerdomain: setting next powerstate for %s to %0x\n", + pr_debug("powerdomain: %s: setting next powerstate to %0x\n", pwrdm->name, pwrst); - prm_rmw_mod_reg_bits(OMAP_POWERSTATE_MASK, - (pwrst << OMAP_POWERSTATE_SHIFT), - pwrdm->prcm_offs, PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_set_next_pwrst) { + /* Trace the pwrdm desired target state */ + trace_power_domain_target(pwrdm->name, pwrst, + smp_processor_id()); + /* Program the pwrdm desired target state */ + ret = arch_pwrdm->pwrdm_set_next_pwrst(pwrdm, pwrst); + } - return 0; + return ret; } /** * pwrdm_read_next_pwrst - get next powerdomain power state * @pwrdm: struct powerdomain * to get power state * - * Return the powerdomain pwrdm's next power state. Returns -EINVAL + * Return the powerdomain @pwrdm's next power state. Returns -EINVAL * if the powerdomain pointer is null or returns the next power state * upon success. */ int pwrdm_read_next_pwrst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; - return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTCTRL, - OMAP_POWERSTATE_MASK); + if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst) + ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm); + + return ret; } /** * pwrdm_read_pwrst - get current powerdomain power state * @pwrdm: struct powerdomain * to get power state * - * Return the powerdomain pwrdm's current power state. Returns -EINVAL + * Return the powerdomain @pwrdm's current power state. Returns -EINVAL * if the powerdomain pointer is null or returns the current power state - * upon success. + * upon success. Note that if the power domain only supports the ON state + * then just return ON as the current state. */ int pwrdm_read_pwrst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; - return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, - OMAP_POWERSTATEST_MASK); + if (pwrdm->pwrsts == PWRSTS_ON) + return PWRDM_POWER_ON; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_pwrst) + ret = arch_pwrdm->pwrdm_read_pwrst(pwrdm); + + return ret; } /** * pwrdm_read_prev_pwrst - get previous powerdomain power state * @pwrdm: struct powerdomain * to get previous power state * - * Return the powerdomain pwrdm's previous power state. Returns -EINVAL + * Return the powerdomain @pwrdm's previous power state. Returns -EINVAL * if the powerdomain pointer is null or returns the previous power state * upon success. */ int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; - return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, - OMAP3430_LASTPOWERSTATEENTERED_MASK); + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_pwrst(pwrdm); + + return ret; } /** @@ -771,34 +685,29 @@ int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm) * @pwrdm: struct powerdomain * to set * @pwrst: one of the PWRDM_POWER_* macros * - * Set the next power state that the logic portion of the powerdomain - * pwrdm will enter when the powerdomain enters retention. This will - * be either RETENTION or OFF, if supported. Returns -EINVAL if the - * powerdomain pointer is null or the target power state is not not - * supported, or returns 0 upon success. + * Set the next power state @pwrst that the logic portion of the + * powerdomain @pwrdm will enter when the powerdomain enters retention. + * This will be either RETENTION or OFF, if supported. Returns + * -EINVAL if the powerdomain pointer is null or the target power + * state is not not supported, or returns 0 upon success. */ int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; if (!(pwrdm->pwrsts_logic_ret & (1 << pwrst))) return -EINVAL; - pr_debug("powerdomain: setting next logic powerstate for %s to %0x\n", + pr_debug("powerdomain: %s: setting next logic powerstate to %0x\n", pwrdm->name, pwrst); - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE, - (pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE)), - pwrdm->prcm_offs, PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst) + ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst); - return 0; + return ret; } /** @@ -807,17 +716,18 @@ int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst) * @bank: memory bank number to set (0-3) * @pwrst: one of the PWRDM_POWER_* macros * - * Set the next power state that memory bank x of the powerdomain - * pwrdm will enter when the powerdomain enters the ON state. Bank - * will be a number from 0 to 3, and represents different types of - * memory, depending on the powerdomain. Returns -EINVAL if the - * powerdomain pointer is null or the target power state is not not - * supported for this memory bank, -EEXIST if the target memory bank - * does not exist or is not controllable, or returns 0 upon success. + * Set the next power state @pwrst that memory bank @bank of the + * powerdomain @pwrdm will enter when the powerdomain enters the ON + * state. @bank will be a number from 0 to 3, and represents different + * types of memory, depending on the powerdomain. Returns -EINVAL if + * the powerdomain pointer is null or the target power state is not + * not supported for this memory bank, -EEXIST if the target memory + * bank does not exist or is not controllable, or returns 0 upon + * success. */ int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) { - u32 m; + int ret = -EINVAL; if (!pwrdm) return -EINVAL; @@ -828,37 +738,13 @@ int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) if (!(pwrdm->pwrsts_mem_on[bank] & (1 << pwrst))) return -EINVAL; - pr_debug("powerdomain: setting next memory powerstate for domain %s " - "bank %0x while pwrdm-ON to %0x\n", pwrdm->name, bank, pwrst); - - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - switch (bank) { - case 0: - m = OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK; - break; - case 1: - m = OMAP3430_L1FLATMEMONSTATE_MASK; - break; - case 2: - m = OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK; - break; - case 3: - m = OMAP3430_L2FLATMEMONSTATE_MASK; - break; - default: - WARN_ON(1); /* should never happen */ - return -EEXIST; - } + pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-ON to %0x\n", + pwrdm->name, bank, pwrst); - prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), - pwrdm->prcm_offs, PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst) + ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst); - return 0; + return ret; } /** @@ -867,18 +753,19 @@ int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) * @bank: memory bank number to set (0-3) * @pwrst: one of the PWRDM_POWER_* macros * - * Set the next power state that memory bank x of the powerdomain - * pwrdm will enter when the powerdomain enters the RETENTION state. - * Bank will be a number from 0 to 3, and represents different types - * of memory, depending on the powerdomain. pwrst will be either - * RETENTION or OFF, if supported. Returns -EINVAL if the powerdomain - * pointer is null or the target power state is not not supported for - * this memory bank, -EEXIST if the target memory bank does not exist - * or is not controllable, or returns 0 upon success. + * Set the next power state @pwrst that memory bank @bank of the + * powerdomain @pwrdm will enter when the powerdomain enters the + * RETENTION state. Bank will be a number from 0 to 3, and represents + * different types of memory, depending on the powerdomain. @pwrst + * will be either RETENTION or OFF, if supported. Returns -EINVAL if + * the powerdomain pointer is null or the target power state is not + * not supported for this memory bank, -EEXIST if the target memory + * bank does not exist or is not controllable, or returns 0 upon + * success. */ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) { - u32 m; + int ret = -EINVAL; if (!pwrdm) return -EINVAL; @@ -889,78 +776,77 @@ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst) if (!(pwrdm->pwrsts_mem_ret[bank] & (1 << pwrst))) return -EINVAL; - pr_debug("powerdomain: setting next memory powerstate for domain %s " - "bank %0x while pwrdm-RET to %0x\n", pwrdm->name, bank, pwrst); + pr_debug("powerdomain: %s: setting next memory powerstate for bank %0x while pwrdm-RET to %0x\n", + pwrdm->name, bank, pwrst); - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - switch (bank) { - case 0: - m = OMAP3430_SHAREDL1CACHEFLATRETSTATE; - break; - case 1: - m = OMAP3430_L1FLATMEMRETSTATE; - break; - case 2: - m = OMAP3430_SHAREDL2CACHEFLATRETSTATE; - break; - case 3: - m = OMAP3430_L2FLATMEMRETSTATE; - break; - default: - WARN_ON(1); /* should never happen */ - return -EEXIST; - } - - prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs, - PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst) + ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst); - return 0; + return ret; } /** * pwrdm_read_logic_pwrst - get current powerdomain logic retention power state * @pwrdm: struct powerdomain * to get current logic retention power state * - * Return the current power state that the logic portion of - * powerdomain pwrdm will enter - * Returns -EINVAL if the powerdomain pointer is null or returns the - * current logic retention power state upon success. + * Return the power state that the logic portion of powerdomain @pwrdm + * will enter when the powerdomain enters retention. Returns -EINVAL + * if the powerdomain pointer is null or returns the logic retention + * power state upon success. */ int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; - return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, - OMAP3430_LOGICSTATEST); + if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst) + ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm); + + return ret; } /** * pwrdm_read_prev_logic_pwrst - get previous powerdomain logic power state * @pwrdm: struct powerdomain * to get previous logic power state * + * Return the powerdomain @pwrdm's previous logic power state. Returns + * -EINVAL if the powerdomain pointer is null or returns the previous + * logic power state upon success. + */ +int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) +{ + int ret = -EINVAL; + + if (!pwrdm) + return -EINVAL; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm); + + return ret; +} + +/** + * pwrdm_read_logic_retst - get next powerdomain logic power state + * @pwrdm: struct powerdomain * to get next logic power state + * * Return the powerdomain pwrdm's logic power state. Returns -EINVAL - * if the powerdomain pointer is null or returns the previous logic + * if the powerdomain pointer is null or returns the next logic * power state upon success. */ -int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) +int pwrdm_read_logic_retst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) return -EINVAL; - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - return prm_read_mod_bits_shift(pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST, - OMAP3430_LASTLOGICSTATEENTERED); + if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst) + ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm); + + return ret; } /** @@ -968,49 +854,28 @@ int pwrdm_read_prev_logic_pwrst(struct powerdomain *pwrdm) * @pwrdm: struct powerdomain * to get current memory bank power state * @bank: memory bank number (0-3) * - * Return the powerdomain pwrdm's current memory power state for bank - * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if + * Return the powerdomain @pwrdm's current memory power state for bank + * @bank. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if * the target memory bank does not exist or is not controllable, or * returns the current memory power state upon success. */ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) { - u32 m; + int ret = -EINVAL; if (!pwrdm) - return -EINVAL; + return ret; if (pwrdm->banks < (bank + 1)) - return -EEXIST; + return ret; if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) bank = 1; - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - switch (bank) { - case 0: - m = OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK; - break; - case 1: - m = OMAP3430_L1FLATMEMSTATEST_MASK; - break; - case 2: - m = OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK; - break; - case 3: - m = OMAP3430_L2FLATMEMSTATEST_MASK; - break; - default: - WARN_ON(1); /* should never happen */ - return -EEXIST; - } + if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_pwrst) + ret = arch_pwrdm->pwrdm_read_mem_pwrst(pwrdm, bank); - return prm_read_mod_bits_shift(pwrdm->prcm_offs, PM_PWSTST, m); + return ret; } /** @@ -1018,77 +883,85 @@ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank) * @pwrdm: struct powerdomain * to get previous memory bank power state * @bank: memory bank number (0-3) * - * Return the powerdomain pwrdm's previous memory power state for bank - * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if - * the target memory bank does not exist or is not controllable, or - * returns the previous memory power state upon success. + * Return the powerdomain @pwrdm's previous memory power state for + * bank @bank. Returns -EINVAL if the powerdomain pointer is null, + * -EEXIST if the target memory bank does not exist or is not + * controllable, or returns the previous memory power state upon + * success. */ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank) { - u32 m; + int ret = -EINVAL; if (!pwrdm) - return -EINVAL; + return ret; if (pwrdm->banks < (bank + 1)) - return -EEXIST; + return ret; if (pwrdm->flags & PWRDM_HAS_MPU_QUIRK) bank = 1; - /* - * The register bit names below may not correspond to the - * actual names of the bits in each powerdomain's register, - * but the type of value returned is the same for each - * powerdomain. - */ - switch (bank) { - case 0: - m = OMAP3430_LASTMEM1STATEENTERED_MASK; - break; - case 1: - m = OMAP3430_LASTMEM2STATEENTERED_MASK; - break; - case 2: - m = OMAP3430_LASTSHAREDL2CACHEFLATSTATEENTERED_MASK; - break; - case 3: - m = OMAP3430_LASTL2FLATMEMSTATEENTERED_MASK; - break; - default: - WARN_ON(1); /* should never happen */ - return -EEXIST; - } + if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_mem_pwrst) + ret = arch_pwrdm->pwrdm_read_prev_mem_pwrst(pwrdm, bank); + + return ret; +} + +/** + * pwrdm_read_mem_retst - get next memory bank power state + * @pwrdm: struct powerdomain * to get mext memory bank power state + * @bank: memory bank number (0-3) + * + * Return the powerdomain pwrdm's next memory power state for bank + * x. Returns -EINVAL if the powerdomain pointer is null, -EEXIST if + * the target memory bank does not exist or is not controllable, or + * returns the next memory power state upon success. + */ +int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank) +{ + int ret = -EINVAL; - return prm_read_mod_bits_shift(pwrdm->prcm_offs, - OMAP3430_PM_PREPWSTST, m); + if (!pwrdm) + return ret; + + if (pwrdm->banks < (bank + 1)) + return ret; + + if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst) + ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank); + + return ret; } /** * pwrdm_clear_all_prev_pwrst - clear previous powerstate register for a pwrdm * @pwrdm: struct powerdomain * to clear * - * Clear the powerdomain's previous power state register. Clears the - * entire register, including logic and memory bank previous power states. - * Returns -EINVAL if the powerdomain pointer is null, or returns 0 upon - * success. + * Clear the powerdomain's previous power state register @pwrdm. + * Clears the entire register, including logic and memory bank + * previous power states. Returns -EINVAL if the powerdomain pointer + * is null, or returns 0 upon success. */ int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) - return -EINVAL; + return ret; /* * XXX should get the powerdomain's current state here; * warn & fail if it is not ON. */ - pr_debug("powerdomain: clearing previous power state reg for %s\n", + pr_debug("powerdomain: %s: clearing previous power state reg\n", pwrdm->name); - prm_write_mod_reg(0, pwrdm->prcm_offs, OMAP3430_PM_PREPWSTST); + if (arch_pwrdm && arch_pwrdm->pwrdm_clear_all_prev_pwrst) + ret = arch_pwrdm->pwrdm_clear_all_prev_pwrst(pwrdm); - return 0; + return ret; } /** @@ -1096,27 +969,28 @@ int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm) * @pwrdm: struct powerdomain * * * Enable automatic context save-and-restore upon power state change - * for some devices in a powerdomain. Warning: this only affects a - * subset of devices in a powerdomain; check the TRM closely. Returns - * -EINVAL if the powerdomain pointer is null or if the powerdomain - * does not support automatic save-and-restore, or returns 0 upon - * success. + * for some devices in the powerdomain @pwrdm. Warning: this only + * affects a subset of devices in a powerdomain; check the TRM + * closely. Returns -EINVAL if the powerdomain pointer is null or if + * the powerdomain does not support automatic save-and-restore, or + * returns 0 upon success. */ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) - return -EINVAL; + return ret; if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) - return -EINVAL; + return ret; - pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", - pwrdm->name); + pr_debug("powerdomain: %s: setting SAVEANDRESTORE bit\n", pwrdm->name); - prm_rmw_mod_reg_bits(0, 1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, - pwrdm->prcm_offs, PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar) + ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm); - return 0; + return ret; } /** @@ -1124,34 +998,35 @@ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm) * @pwrdm: struct powerdomain * * * Disable automatic context save-and-restore upon power state change - * for some devices in a powerdomain. Warning: this only affects a - * subset of devices in a powerdomain; check the TRM closely. Returns - * -EINVAL if the powerdomain pointer is null or if the powerdomain - * does not support automatic save-and-restore, or returns 0 upon - * success. + * for some devices in the powerdomain @pwrdm. Warning: this only + * affects a subset of devices in a powerdomain; check the TRM + * closely. Returns -EINVAL if the powerdomain pointer is null or if + * the powerdomain does not support automatic save-and-restore, or + * returns 0 upon success. */ int pwrdm_disable_hdwr_sar(struct powerdomain *pwrdm) { + int ret = -EINVAL; + if (!pwrdm) - return -EINVAL; + return ret; if (!(pwrdm->flags & PWRDM_HAS_HDWR_SAR)) - return -EINVAL; + return ret; - pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", - pwrdm->name); + pr_debug("powerdomain: %s: clearing SAVEANDRESTORE bit\n", pwrdm->name); - prm_rmw_mod_reg_bits(1 << OMAP3430ES2_SAVEANDRESTORE_SHIFT, 0, - pwrdm->prcm_offs, PM_PWSTCTRL); + if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar) + ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm); - return 0; + return ret; } /** * pwrdm_has_hdwr_sar - test whether powerdomain supports hardware SAR * @pwrdm: struct powerdomain * * - * Returns 1 if powerdomain 'pwrdm' supports hardware save-and-restore + * Returns 1 if powerdomain @pwrdm supports hardware save-and-restore * for some devices, or 0 if it does not. */ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) @@ -1159,76 +1034,180 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm) return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; } -/** - * 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 pwrdm_state_switch_nolock(struct powerdomain *pwrdm) { - u32 c = 0; + int ret; - if (!pwrdm) + if (!pwrdm || !arch_pwrdm) return -EINVAL; - /* - * REVISIT: pwrdm_wait_transition() may be better implemented - * via a callback and a periodic timer check -- how long do we expect - * powerdomain transitions to take? - */ + ret = arch_pwrdm->pwrdm_wait_transition(pwrdm); + if (!ret) + ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); - /* XXX Is this udelay() value meaningful? */ - while ((prm_read_mod_reg(pwrdm->prcm_offs, PM_PWSTST) & - OMAP_INTRANSITION) && - (c++ < PWRDM_TRANSITION_BAILOUT)) - udelay(1); + return ret; +} - if (c > PWRDM_TRANSITION_BAILOUT) { - printk(KERN_ERR "powerdomain: waited too long for " - "powerdomain %s to complete transition\n", pwrdm->name); - return -EAGAIN; - } +int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm) +{ + int ret; - pr_debug("powerdomain: completed transition in %d loops\n", c); + pwrdm_lock(pwrdm); + ret = pwrdm_state_switch_nolock(pwrdm); + pwrdm_unlock(pwrdm); - return 0; + return ret; } -int pwrdm_state_switch(struct powerdomain *pwrdm) +int pwrdm_pre_transition(struct powerdomain *pwrdm) { - return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); + if (pwrdm) + _pwrdm_pre_transition_cb(pwrdm, NULL); + else + pwrdm_for_each(_pwrdm_pre_transition_cb, NULL); + + return 0; } -int pwrdm_clkdm_state_switch(struct clockdomain *clkdm) +int pwrdm_post_transition(struct powerdomain *pwrdm) { - if (clkdm != NULL && clkdm->pwrdm.ptr != NULL) { - pwrdm_wait_transition(clkdm->pwrdm.ptr); - return pwrdm_state_switch(clkdm->pwrdm.ptr); - } + if (pwrdm) + _pwrdm_post_transition_cb(pwrdm, NULL); + else + pwrdm_for_each(_pwrdm_post_transition_cb, NULL); - return -EINVAL; + return 0; } -int pwrdm_clk_state_switch(struct clk *clk) + +/** + * 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) { - if (clk != NULL && clk->clkdm != NULL) - return pwrdm_clkdm_state_switch(clk->clkdm); - return -EINVAL; + u8 next_pwrst, sleep_switch; + int curr_pwrst; + 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); + if (curr_pwrst < 0) { + ret = -EINVAL; + goto osps_out; + } + + 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); + + 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; } -int pwrdm_pre_transition(void) +/** + * pwrdm_get_context_loss_count - get powerdomain's context loss count + * @pwrdm: struct powerdomain * to wait for + * + * Context loss count is the sum of powerdomain off-mode counter, the + * logic off counter and the per-bank memory off counter. Returns negative + * (and WARNs) upon error, otherwise, returns the context loss count. + */ +int pwrdm_get_context_loss_count(struct powerdomain *pwrdm) { - pwrdm_for_each(_pwrdm_pre_transition_cb, NULL); - return 0; + int i, count; + + if (!pwrdm) { + WARN(1, "powerdomain: %s: pwrdm is null\n", __func__); + return -ENODEV; + } + + count = pwrdm->state_counter[PWRDM_POWER_OFF]; + count += pwrdm->ret_logic_off_counter; + + for (i = 0; i < pwrdm->banks; i++) + count += pwrdm->ret_mem_off_counter[i]; + + /* + * Context loss count has to be a non-negative value. Clear the sign + * bit to get a value range from 0 to INT_MAX. + */ + count &= INT_MAX; + + pr_debug("powerdomain: %s: context loss count = %d\n", + pwrdm->name, count); + + return count; } -int pwrdm_post_transition(void) +/** + * pwrdm_can_ever_lose_context - can this powerdomain ever lose context? + * @pwrdm: struct powerdomain * + * + * Given a struct powerdomain * @pwrdm, returns 1 if the powerdomain + * can lose either memory or logic context or if @pwrdm is invalid, or + * returns 0 otherwise. This function is not concerned with how the + * powerdomain registers are programmed (i.e., to go off or not); it's + * concerned with whether it's ever possible for this powerdomain to + * go off while some other part of the chip is active. This function + * assumes that every powerdomain can go to either ON or INACTIVE. + */ +bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm) { - pwrdm_for_each(_pwrdm_post_transition_cb, NULL); + int i; + + if (!pwrdm) { + pr_debug("powerdomain: %s: invalid powerdomain pointer\n", + __func__); + return 1; + } + + if (pwrdm->pwrsts & PWRSTS_OFF) + return 1; + + if (pwrdm->pwrsts & PWRSTS_RET) { + if (pwrdm->pwrsts_logic_ret & PWRSTS_OFF) + return 1; + + for (i = 0; i < pwrdm->banks; i++) + if (pwrdm->pwrsts_mem_ret[i] & PWRSTS_OFF) + return 1; + } + + for (i = 0; i < pwrdm->banks; i++) + if (pwrdm->pwrsts_mem_on[i] & PWRSTS_OFF) + return 1; + return 0; } - |
