aboutsummaryrefslogtreecommitdiff
path: root/arch/arm/mach-omap2/powerdomain.c
diff options
context:
space:
mode:
Diffstat (limited to 'arch/arm/mach-omap2/powerdomain.c')
-rw-r--r--arch/arm/mach-omap2/powerdomain.c860
1 files changed, 498 insertions, 362 deletions
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 6527ec30dc1..faebd5f076a 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -1,8 +1,8 @@
/*
* OMAP powerdomain control
*
- * Copyright (C) 2007-2008 Texas Instruments, Inc.
- * Copyright (C) 2007-2009 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>
@@ -15,71 +15,50 @@
#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 "cm-regbits-44xx.h"
-#include "prm.h"
-#include "prm-regbits-34xx.h"
-#include "prm-regbits-44xx.h"
+#include <asm/cpu.h>
-#include <plat/cpu.h>
-#include <plat/powerdomain.h>
-#include <plat/clockdomain.h>
-#include <plat/prcm.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,
};
-/* Variable holding value of the CPU dependent PWRSTCTRL Register Offset */
-static u16 pwrstctrl_reg_offs;
-
-/* Variable holding value of the CPU dependent PWRSTST Register Offset */
-static u16 pwrstst_reg_offs;
-
-/* OMAP3 and OMAP4 specific register bit initialisations
- * Notice that the names here are not according to each power
- * domain but the bit mapping used applies to all of them
+/*
+ * Types of sleep_switch used internally in omap_set_pwrdm_state()
+ * and its associated static functions
+ *
+ * XXX Better documentation is needed here
*/
-
-/* OMAP3 and OMAP4 Memory Onstate Masks (common across all power domains) */
-#define OMAP_MEM0_ONSTATE_MASK OMAP3430_SHAREDL1CACHEFLATONSTATE_MASK
-#define OMAP_MEM1_ONSTATE_MASK OMAP3430_L1FLATMEMONSTATE_MASK
-#define OMAP_MEM2_ONSTATE_MASK OMAP3430_SHAREDL2CACHEFLATONSTATE_MASK
-#define OMAP_MEM3_ONSTATE_MASK OMAP3430_L2FLATMEMONSTATE_MASK
-#define OMAP_MEM4_ONSTATE_MASK OMAP4430_OCP_NRET_BANK_ONSTATE_MASK
-
-/* OMAP3 and OMAP4 Memory Retstate Masks (common across all power domains) */
-#define OMAP_MEM0_RETSTATE_MASK OMAP3430_SHAREDL1CACHEFLATRETSTATE_MASK
-#define OMAP_MEM1_RETSTATE_MASK OMAP3430_L1FLATMEMRETSTATE_MASK
-#define OMAP_MEM2_RETSTATE_MASK OMAP3430_SHAREDL2CACHEFLATRETSTATE_MASK
-#define OMAP_MEM3_RETSTATE_MASK OMAP3430_L2FLATMEMRETSTATE_MASK
-#define OMAP_MEM4_RETSTATE_MASK OMAP4430_OCP_NRET_BANK_RETSTATE_MASK
-
-/* OMAP3 and OMAP4 Memory Status bits */
-#define OMAP_MEM0_STATEST_MASK OMAP3430_SHAREDL1CACHEFLATSTATEST_MASK
-#define OMAP_MEM1_STATEST_MASK OMAP3430_L1FLATMEMSTATEST_MASK
-#define OMAP_MEM2_STATEST_MASK OMAP3430_SHAREDL2CACHEFLATSTATEST_MASK
-#define OMAP_MEM3_STATEST_MASK OMAP3430_L2FLATMEMSTATEST_MASK
-#define OMAP_MEM4_STATEST_MASK OMAP4430_OCP_NRET_BANK_STATEST_MASK
+#define ALREADYACTIVE_SWITCH 0
+#define FORCEWAKEUP_SWITCH 1
+#define LOWPOWERSTATE_SWITCH 2
/* pwrdm_list contains all registered struct powerdomains */
static LIST_HEAD(pwrdm_list);
+static struct pwrdm_ops *arch_pwrdm;
+
/* Private functions */
static struct powerdomain *_pwrdm_lookup(const char *name)
@@ -109,16 +88,37 @@ static struct powerdomain *_pwrdm_lookup(const char *name)
static int _pwrdm_register(struct powerdomain *pwrdm)
{
int i;
+ struct voltagedomain *voltdm;
- if (!pwrdm)
+ if (!pwrdm || !pwrdm->name)
return -EINVAL;
- if (!omap_chip_is(pwrdm->omap_chip))
+ 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 (_pwrdm_lookup(pwrdm->name))
return -EEXIST;
+ 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 */
@@ -129,7 +129,8 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
for (i = 0; i < pwrdm->banks; i++)
pwrdm->ret_mem_off_counter[i] = 0;
- pwrdm_wait_transition(pwrdm);
+ 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;
@@ -160,8 +161,7 @@ static void _update_logic_membank_counters(struct powerdomain *pwrdm)
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;
@@ -178,6 +178,18 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
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;
@@ -206,38 +218,174 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
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 < 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 */
/**
- * pwrdm_init - set up the powerdomain layer
- * @pwrdm_list: array of struct powerdomain pointers to register
+ * pwrdm_register_platform_funcs - register powerdomain implementation fns
+ * @po: func pointers for arch specific implementations
*
- * Loop through the array of powerdomains @pwrdm_list, 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. XXX pwrdm_list is not really a
- * "list"; it is an array. Rename appropriately.
+ * 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)
+{
+ if (!po)
+ return -EINVAL;
+
+ if (arch_pwrdm)
+ return -EEXIST;
+
+ arch_pwrdm = po;
+
+ return 0;
+}
+
+/**
+ * pwrdm_register_pwrdms - register SoC powerdomains
+ * @ps: pointer to an array of struct powerdomain to register
+ *
+ * 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_pwrdms(struct powerdomain **ps)
{
struct powerdomain **p = NULL;
- if (cpu_is_omap24xx() || cpu_is_omap34xx()) {
- pwrstctrl_reg_offs = OMAP2_PM_PWSTCTRL;
- pwrstst_reg_offs = OMAP2_PM_PWSTST;
- } else if (cpu_is_omap44xx()) {
- pwrstctrl_reg_offs = OMAP4_PM_PWSTCTRL;
- pwrstst_reg_offs = OMAP4_PM_PWSTST;
- } else {
- printk(KERN_ERR "Power Domain struct not supported for " \
- "this CPU\n");
- return;
- }
+ if (!arch_pwrdm)
+ return -EEXIST;
- if (pwrdm_list) {
- for (p = pwrdm_list; *p; p++)
- _pwrdm_register(*p);
- }
+ if (!ps)
+ return -EINVAL;
+
+ for (p = ps; *p; p++)
+ _pwrdm_register(*p);
+
+ return 0;
+}
+
+/**
+ * pwrdm_complete_init - set up the powerdomain layer
+ *
+ * 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_complete_init(void)
+{
+ struct powerdomain *temp_p;
+
+ if (list_empty(&pwrdm_list))
+ return -EACCES;
+
+ 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);
}
/**
@@ -305,8 +453,8 @@ int pwrdm_add_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
if (!pwrdm || !clkdm)
return -EINVAL;
- pr_debug("powerdomain: associating clockdomain %s with powerdomain "
- "%s\n", clkdm->name, pwrdm->name);
+ 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])
@@ -320,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;
@@ -353,16 +501,16 @@ int pwrdm_del_clkdm(struct powerdomain *pwrdm, struct clockdomain *clkdm)
if (!pwrdm || !clkdm)
return -EINVAL;
- pr_debug("powerdomain: dissociating clockdomain %s from powerdomain "
- "%s\n", clkdm->name, pwrdm->name);
+ 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;
}
@@ -404,6 +552,18 @@ int pwrdm_for_each_clkdm(struct powerdomain *pwrdm,
}
/**
+ * pwrdm_get_voltdm - return a ptr to the voltdm that this pwrdm resides in
+ * @pwrdm: struct powerdomain *
+ *
+ * Return a pointer to the struct voltageomain that the specified powerdomain
+ * @pwrdm exists in.
+ */
+struct voltagedomain *pwrdm_get_voltdm(struct powerdomain *pwrdm)
+{
+ return pwrdm->voltdm.ptr;
+}
+
+/**
* pwrdm_get_mem_bank_count - get number of memory banks in this powerdomain
* @pwrdm: struct powerdomain *
*
@@ -431,20 +591,26 @@ 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, pwrstctrl_reg_offs);
+ 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;
}
/**
@@ -457,11 +623,15 @@ int pwrdm_set_next_pwrst(struct powerdomain *pwrdm, u8 pwrst)
*/
int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
{
+ int ret = -EINVAL;
+
if (!pwrdm)
return -EINVAL;
- return prm_read_mod_bits_shift(pwrdm->prcm_offs,
- pwrstctrl_reg_offs, OMAP_POWERSTATE_MASK);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_read_next_pwrst)
+ ret = arch_pwrdm->pwrdm_read_next_pwrst(pwrdm);
+
+ return ret;
}
/**
@@ -470,15 +640,23 @@ int pwrdm_read_next_pwrst(struct powerdomain *pwrdm)
*
* 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,
- pwrstst_reg_offs, 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;
}
/**
@@ -491,11 +669,15 @@ int pwrdm_read_pwrst(struct powerdomain *pwrdm)
*/
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;
}
/**
@@ -511,7 +693,7 @@ int pwrdm_read_prev_pwrst(struct powerdomain *pwrdm)
*/
int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
{
- u32 v;
+ int ret = -EINVAL;
if (!pwrdm)
return -EINVAL;
@@ -519,20 +701,13 @@ int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
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.
- */
- v = pwrst << __ffs(OMAP3430_LOGICL1CACHERETSTATE_MASK);
- prm_rmw_mod_reg_bits(OMAP3430_LOGICL1CACHERETSTATE_MASK, v,
- pwrdm->prcm_offs, pwrstctrl_reg_offs);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_set_logic_retst)
+ ret = arch_pwrdm->pwrdm_set_logic_retst(pwrdm, pwrst);
- return 0;
+ return ret;
}
/**
@@ -552,7 +727,7 @@ int pwrdm_set_logic_retst(struct powerdomain *pwrdm, u8 pwrst)
*/
int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
{
- u32 m;
+ int ret = -EINVAL;
if (!pwrdm)
return -EINVAL;
@@ -563,40 +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);
+ pr_debug("powerdomain: %s: setting next memory powerstate for 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 = OMAP_MEM0_ONSTATE_MASK;
- break;
- case 1:
- m = OMAP_MEM1_ONSTATE_MASK;
- break;
- case 2:
- m = OMAP_MEM2_ONSTATE_MASK;
- break;
- case 3:
- m = OMAP_MEM3_ONSTATE_MASK;
- break;
- case 4:
- m = OMAP_MEM4_ONSTATE_MASK;
- break;
- default:
- WARN_ON(1); /* should never happen */
- return -EEXIST;
- }
-
- prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)),
- pwrdm->prcm_offs, pwrstctrl_reg_offs);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_onst)
+ ret = arch_pwrdm->pwrdm_set_mem_onst(pwrdm, bank, pwrst);
- return 0;
+ return ret;
}
/**
@@ -617,7 +765,7 @@ int pwrdm_set_mem_onst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
*/
int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
{
- u32 m;
+ int ret = -EINVAL;
if (!pwrdm)
return -EINVAL;
@@ -628,40 +776,13 @@ 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 = OMAP_MEM0_RETSTATE_MASK;
- break;
- case 1:
- m = OMAP_MEM1_RETSTATE_MASK;
- break;
- case 2:
- m = OMAP_MEM2_RETSTATE_MASK;
- break;
- case 3:
- m = OMAP_MEM3_RETSTATE_MASK;
- break;
- case 4:
- m = OMAP_MEM4_RETSTATE_MASK;
- break;
- default:
- WARN_ON(1); /* should never happen */
- return -EEXIST;
- }
+ if (arch_pwrdm && arch_pwrdm->pwrdm_set_mem_retst)
+ ret = arch_pwrdm->pwrdm_set_mem_retst(pwrdm, bank, pwrst);
- prm_rmw_mod_reg_bits(m, (pwrst << __ffs(m)), pwrdm->prcm_offs,
- pwrstctrl_reg_offs);
-
- return 0;
+ return ret;
}
/**
@@ -675,11 +796,15 @@ int pwrdm_set_mem_retst(struct powerdomain *pwrdm, u8 bank, u8 pwrst)
*/
int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
{
+ int ret = -EINVAL;
+
if (!pwrdm)
return -EINVAL;
- return prm_read_mod_bits_shift(pwrdm->prcm_offs, pwrstst_reg_offs,
- OMAP3430_LOGICSTATEST_MASK);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_pwrst)
+ ret = arch_pwrdm->pwrdm_read_logic_pwrst(pwrdm);
+
+ return ret;
}
/**
@@ -692,17 +817,15 @@ int pwrdm_read_logic_pwrst(struct powerdomain *pwrdm)
*/
int pwrdm_read_prev_logic_pwrst(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_MASK);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_read_prev_logic_pwrst)
+ ret = arch_pwrdm->pwrdm_read_prev_logic_pwrst(pwrdm);
+
+ return ret;
}
/**
@@ -715,17 +838,15 @@ 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, pwrstctrl_reg_offs,
- OMAP3430_LOGICSTATEST_MASK);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_read_logic_retst)
+ ret = arch_pwrdm->pwrdm_read_logic_retst(pwrdm);
+
+ return ret;
}
/**
@@ -740,46 +861,21 @@ int pwrdm_read_logic_retst(struct powerdomain *pwrdm)
*/
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 = OMAP_MEM0_STATEST_MASK;
- break;
- case 1:
- m = OMAP_MEM1_STATEST_MASK;
- break;
- case 2:
- m = OMAP_MEM2_STATEST_MASK;
- break;
- case 3:
- m = OMAP_MEM3_STATEST_MASK;
- break;
- case 4:
- m = OMAP_MEM4_STATEST_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,
- pwrstst_reg_offs, m);
+ return ret;
}
/**
@@ -795,43 +891,21 @@ int pwrdm_read_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
*/
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 prm_read_mod_bits_shift(pwrdm->prcm_offs,
- OMAP3430_PM_PREPWSTST, m);
+ return ret;
}
/**
@@ -846,43 +920,18 @@ int pwrdm_read_prev_mem_pwrst(struct powerdomain *pwrdm, u8 bank)
*/
int pwrdm_read_mem_retst(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;
- /*
- * 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 = OMAP_MEM0_RETSTATE_MASK;
- break;
- case 1:
- m = OMAP_MEM1_RETSTATE_MASK;
- break;
- case 2:
- m = OMAP_MEM2_RETSTATE_MASK;
- break;
- case 3:
- m = OMAP_MEM3_RETSTATE_MASK;
- break;
- case 4:
- m = OMAP_MEM4_RETSTATE_MASK;
- break;
- default:
- WARN_ON(1); /* should never happen */
- return -EEXIST;
- }
+ if (arch_pwrdm && arch_pwrdm->pwrdm_read_mem_retst)
+ ret = arch_pwrdm->pwrdm_read_mem_retst(pwrdm, bank);
- return prm_read_mod_bits_shift(pwrdm->prcm_offs,
- pwrstctrl_reg_offs, m);
+ return ret;
}
/**
@@ -896,20 +945,23 @@ int pwrdm_read_mem_retst(struct powerdomain *pwrdm, u8 bank)
*/
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;
}
/**
@@ -925,19 +977,20 @@ int pwrdm_clear_all_prev_pwrst(struct powerdomain *pwrdm)
*/
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, pwrstctrl_reg_offs);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_enable_hdwr_sar)
+ ret = arch_pwrdm->pwrdm_enable_hdwr_sar(pwrdm);
- return 0;
+ return ret;
}
/**
@@ -953,19 +1006,20 @@ int pwrdm_enable_hdwr_sar(struct powerdomain *pwrdm)
*/
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, pwrstctrl_reg_offs);
+ if (arch_pwrdm && arch_pwrdm->pwrdm_disable_hdwr_sar)
+ ret = arch_pwrdm->pwrdm_disable_hdwr_sar(pwrdm);
- return 0;
+ return ret;
}
/**
@@ -980,98 +1034,180 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
}
-/**
- * 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 pwrdm_state_switch_nolock(struct powerdomain *pwrdm)
{
- if (!pwrdm)
- return -EINVAL;
+ int ret;
- if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
+ if (!pwrdm || !arch_pwrdm)
return -EINVAL;
- pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
- pwrdm->name);
+ ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
+ if (!ret)
+ ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
- prm_rmw_mod_reg_bits(OMAP4430_LOWPOWERSTATECHANGE_MASK,
- (1 << OMAP4430_LOWPOWERSTATECHANGE_SHIFT),
- pwrdm->prcm_offs, pwrstctrl_reg_offs);
+ return ret;
+}
+
+int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm)
+{
+ int ret;
+
+ pwrdm_lock(pwrdm);
+ ret = pwrdm_state_switch_nolock(pwrdm);
+ pwrdm_unlock(pwrdm);
+
+ return ret;
+}
+
+int pwrdm_pre_transition(struct powerdomain *pwrdm)
+{
+ if (pwrdm)
+ _pwrdm_pre_transition_cb(pwrdm, NULL);
+ else
+ pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
+
+ return 0;
+}
+
+int pwrdm_post_transition(struct powerdomain *pwrdm)
+{
+ if (pwrdm)
+ _pwrdm_post_transition_cb(pwrdm, NULL);
+ else
+ pwrdm_for_each(_pwrdm_post_transition_cb, NULL);
return 0;
}
/**
- * pwrdm_wait_transition - wait for powerdomain power transition to finish
- * @pwrdm: struct powerdomain * to wait for
+ * 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
*
- * 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.
+ * 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 pwrdm_wait_transition(struct powerdomain *pwrdm)
+int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
{
- u32 c = 0;
+ u8 next_pwrst, sleep_switch;
+ int curr_pwrst;
+ int ret = 0;
+ bool hwsup = false;
- if (!pwrdm)
+ if (!pwrdm || IS_ERR(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?
- */
+ while (!(pwrdm->pwrsts & (1 << pwrst))) {
+ if (pwrst == PWRDM_POWER_OFF)
+ return ret;
+ pwrst--;
+ }
- /* XXX Is this udelay() value meaningful? */
- while ((prm_read_mod_reg(pwrdm->prcm_offs, pwrstst_reg_offs) &
- OMAP_INTRANSITION_MASK) &&
- (c++ < PWRDM_TRANSITION_BAILOUT))
- udelay(1);
+ pwrdm_lock(pwrdm);
- if (c > PWRDM_TRANSITION_BAILOUT) {
- printk(KERN_ERR "powerdomain: waited too long for "
- "powerdomain %s to complete transition\n", pwrdm->name);
- return -EAGAIN;
+ curr_pwrst = pwrdm_read_pwrst(pwrdm);
+ if (curr_pwrst < 0) {
+ ret = -EINVAL;
+ goto osps_out;
}
- pr_debug("powerdomain: completed transition in %d loops\n", c);
+ next_pwrst = pwrdm_read_next_pwrst(pwrdm);
+ if (curr_pwrst == pwrst && next_pwrst == pwrst)
+ goto osps_out;
- return 0;
-}
+ sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
+ pwrst, &hwsup);
-int pwrdm_state_switch(struct powerdomain *pwrdm)
-{
- return _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
+ 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_clkdm_state_switch(struct clockdomain *clkdm)
+/**
+ * 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)
{
- if (clkdm != NULL && clkdm->pwrdm.ptr != NULL) {
- pwrdm_wait_transition(clkdm->pwrdm.ptr);
- return pwrdm_state_switch(clkdm->pwrdm.ptr);
+ int i, count;
+
+ if (!pwrdm) {
+ WARN(1, "powerdomain: %s: pwrdm is null\n", __func__);
+ return -ENODEV;
}
- return -EINVAL;
-}
+ count = pwrdm->state_counter[PWRDM_POWER_OFF];
+ count += pwrdm->ret_logic_off_counter;
-int pwrdm_pre_transition(void)
-{
- pwrdm_for_each(_pwrdm_pre_transition_cb, NULL);
- return 0;
+ 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;
}
-