aboutsummaryrefslogtreecommitdiffstats
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.c232
1 files changed, 180 insertions, 52 deletions
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 36a69189b083..43ac20c15f4f 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -19,6 +19,7 @@
19#include <linux/list.h> 19#include <linux/list.h>
20#include <linux/errno.h> 20#include <linux/errno.h>
21#include <linux/string.h> 21#include <linux/string.h>
22#include <linux/spinlock.h>
22#include <trace/events/power.h> 23#include <trace/events/power.h>
23 24
24#include "cm2xxx_3xxx.h" 25#include "cm2xxx_3xxx.h"
@@ -42,6 +43,16 @@ enum {
42 PWRDM_STATE_PREV, 43 PWRDM_STATE_PREV,
43}; 44};
44 45
46/*
47 * Types of sleep_switch used internally in omap_set_pwrdm_state()
48 * and its associated static functions
49 *
50 * XXX Better documentation is needed here
51 */
52#define ALREADYACTIVE_SWITCH 0
53#define FORCEWAKEUP_SWITCH 1
54#define LOWPOWERSTATE_SWITCH 2
55#define ERROR_SWITCH 3
45 56
46/* pwrdm_list contains all registered struct powerdomains */ 57/* pwrdm_list contains all registered struct powerdomains */
47static LIST_HEAD(pwrdm_list); 58static LIST_HEAD(pwrdm_list);
@@ -101,6 +112,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
101 pwrdm->voltdm.ptr = voltdm; 112 pwrdm->voltdm.ptr = voltdm;
102 INIT_LIST_HEAD(&pwrdm->voltdm_node); 113 INIT_LIST_HEAD(&pwrdm->voltdm_node);
103 voltdm_add_pwrdm(voltdm, pwrdm); 114 voltdm_add_pwrdm(voltdm, pwrdm);
115 spin_lock_init(&pwrdm->_lock);
104 116
105 list_add(&pwrdm->node, &pwrdm_list); 117 list_add(&pwrdm->node, &pwrdm_list);
106 118
@@ -112,7 +124,7 @@ static int _pwrdm_register(struct powerdomain *pwrdm)
112 for (i = 0; i < pwrdm->banks; i++) 124 for (i = 0; i < pwrdm->banks; i++)
113 pwrdm->ret_mem_off_counter[i] = 0; 125 pwrdm->ret_mem_off_counter[i] = 0;
114 126
115 pwrdm_wait_transition(pwrdm); 127 arch_pwrdm->pwrdm_wait_transition(pwrdm);
116 pwrdm->state = pwrdm_read_pwrst(pwrdm); 128 pwrdm->state = pwrdm_read_pwrst(pwrdm);
117 pwrdm->state_counter[pwrdm->state] = 1; 129 pwrdm->state_counter[pwrdm->state] = 1;
118 130
@@ -143,7 +155,7 @@ static void _update_logic_membank_counters(struct powerdomain *pwrdm)
143static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag) 155static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
144{ 156{
145 157
146 int prev, state, trace_state = 0; 158 int prev, next, state, trace_state = 0;
147 159
148 if (pwrdm == NULL) 160 if (pwrdm == NULL)
149 return -EINVAL; 161 return -EINVAL;
@@ -164,9 +176,10 @@ static int _pwrdm_state_switch(struct powerdomain *pwrdm, int flag)
164 * If the power domain did not hit the desired state, 176 * If the power domain did not hit the desired state,
165 * generate a trace event with both the desired and hit states 177 * generate a trace event with both the desired and hit states
166 */ 178 */
167 if (state != prev) { 179 next = pwrdm_read_next_pwrst(pwrdm);
180 if (next != prev) {
168 trace_state = (PWRDM_TRACE_STATES_FLAG | 181 trace_state = (PWRDM_TRACE_STATES_FLAG |
169 ((state & OMAP_POWERSTATE_MASK) << 8) | 182 ((next & OMAP_POWERSTATE_MASK) << 8) |
170 ((prev & OMAP_POWERSTATE_MASK) << 0)); 183 ((prev & OMAP_POWERSTATE_MASK) << 0));
171 trace_power_domain_target(pwrdm->name, trace_state, 184 trace_power_domain_target(pwrdm->name, trace_state,
172 smp_processor_id()); 185 smp_processor_id());
@@ -199,6 +212,80 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
199 return 0; 212 return 0;
200} 213}
201 214
215/**
216 * _pwrdm_save_clkdm_state_and_activate - prepare for power state change
217 * @pwrdm: struct powerdomain * to operate on
218 * @curr_pwrst: current power state of @pwrdm
219 * @pwrst: power state to switch to
220 * @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
221 *
222 * Determine whether the powerdomain needs to be turned on before
223 * attempting to switch power states. Called by
224 * omap_set_pwrdm_state(). NOTE that if the powerdomain contains
225 * multiple clockdomains, this code assumes that the first clockdomain
226 * supports software-supervised wakeup mode - potentially a problem.
227 * Returns the power state switch mode currently in use (see the
228 * "Types of sleep_switch" comment above).
229 */
230static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
231 u8 curr_pwrst, u8 pwrst,
232 bool *hwsup)
233{
234 u8 sleep_switch;
235
236 if (curr_pwrst < 0) {
237 WARN_ON(1);
238 sleep_switch = ERROR_SWITCH;
239 } else if (curr_pwrst < PWRDM_POWER_ON) {
240 if (curr_pwrst > pwrst &&
241 pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
242 arch_pwrdm->pwrdm_set_lowpwrstchange) {
243 sleep_switch = LOWPOWERSTATE_SWITCH;
244 } else {
245 *hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
246 clkdm_wakeup_nolock(pwrdm->pwrdm_clkdms[0]);
247 sleep_switch = FORCEWAKEUP_SWITCH;
248 }
249 } else {
250 sleep_switch = ALREADYACTIVE_SWITCH;
251 }
252
253 return sleep_switch;
254}
255
256/**
257 * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
258 * @pwrdm: struct powerdomain * to operate on
259 * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
260 * @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
261 *
262 * Restore the clockdomain state perturbed by
263 * _pwrdm_save_clkdm_state_and_activate(), and call the power state
264 * bookkeeping code. Called by omap_set_pwrdm_state(). NOTE that if
265 * the powerdomain contains multiple clockdomains, this assumes that
266 * the first associated clockdomain supports either
267 * hardware-supervised idle control in the register, or
268 * software-supervised sleep. No return value.
269 */
270static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
271 u8 sleep_switch, bool hwsup)
272{
273 switch (sleep_switch) {
274 case FORCEWAKEUP_SWITCH:
275 if (hwsup)
276 clkdm_allow_idle_nolock(pwrdm->pwrdm_clkdms[0]);
277 else
278 clkdm_sleep_nolock(pwrdm->pwrdm_clkdms[0]);
279 break;
280 case LOWPOWERSTATE_SWITCH:
281 if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
282 arch_pwrdm->pwrdm_set_lowpwrstchange)
283 arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
284 pwrdm_state_switch_nolock(pwrdm);
285 break;
286 }
287}
288
202/* Public functions */ 289/* Public functions */
203 290
204/** 291/**
@@ -275,6 +362,30 @@ int pwrdm_complete_init(void)
275} 362}
276 363
277/** 364/**
365 * pwrdm_lock - acquire a Linux spinlock on a powerdomain
366 * @pwrdm: struct powerdomain * to lock
367 *
368 * Acquire the powerdomain spinlock on @pwrdm. No return value.
369 */
370void pwrdm_lock(struct powerdomain *pwrdm)
371 __acquires(&pwrdm->_lock)
372{
373 spin_lock_irqsave(&pwrdm->_lock, pwrdm->_lock_flags);
374}
375
376/**
377 * pwrdm_unlock - release a Linux spinlock on a powerdomain
378 * @pwrdm: struct powerdomain * to unlock
379 *
380 * Release the powerdomain spinlock on @pwrdm. No return value.
381 */
382void pwrdm_unlock(struct powerdomain *pwrdm)
383 __releases(&pwrdm->_lock)
384{
385 spin_unlock_irqrestore(&pwrdm->_lock, pwrdm->_lock_flags);
386}
387
388/**
278 * pwrdm_lookup - look up a powerdomain by name, return a pointer 389 * pwrdm_lookup - look up a powerdomain by name, return a pointer
279 * @name: name of powerdomain 390 * @name: name of powerdomain
280 * 391 *
@@ -920,65 +1031,27 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
920 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; 1031 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
921} 1032}
922 1033
923/** 1034int pwrdm_state_switch_nolock(struct powerdomain *pwrdm)
924 * pwrdm_set_lowpwrstchange - Request a low power state change
925 * @pwrdm: struct powerdomain *
926 *
927 * Allows a powerdomain to transtion to a lower power sleep state
928 * from an existing sleep state without waking up the powerdomain.
929 * Returns -EINVAL if the powerdomain pointer is null or if the
930 * powerdomain does not support LOWPOWERSTATECHANGE, or returns 0
931 * upon success.
932 */
933int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm)
934{
935 int ret = -EINVAL;
936
937 if (!pwrdm)
938 return -EINVAL;
939
940 if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
941 return -EINVAL;
942
943 pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
944 pwrdm->name);
945
946 if (arch_pwrdm && arch_pwrdm->pwrdm_set_lowpwrstchange)
947 ret = arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
948
949 return ret;
950}
951
952/**
953 * pwrdm_wait_transition - wait for powerdomain power transition to finish
954 * @pwrdm: struct powerdomain * to wait for
955 *
956 * If the powerdomain @pwrdm is in the process of a state transition,
957 * spin until it completes the power transition, or until an iteration
958 * bailout value is reached. Returns -EINVAL if the powerdomain
959 * pointer is null, -EAGAIN if the bailout value was reached, or
960 * returns 0 upon success.
961 */
962int pwrdm_wait_transition(struct powerdomain *pwrdm)
963{ 1035{
964 int ret = -EINVAL; 1036 int ret;
965 1037
966 if (!pwrdm) 1038 if (!pwrdm || !arch_pwrdm)
967 return -EINVAL; 1039 return -EINVAL;
968 1040
969 if (arch_pwrdm && arch_pwrdm->pwrdm_wait_transition) 1041 ret = arch_pwrdm->pwrdm_wait_transition(pwrdm);
970 ret = arch_pwrdm->pwrdm_wait_transition(pwrdm); 1042 if (!ret)
1043 ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW);
971 1044
972 return ret; 1045 return ret;
973} 1046}
974 1047
975int pwrdm_state_switch(struct powerdomain *pwrdm) 1048int __deprecated pwrdm_state_switch(struct powerdomain *pwrdm)
976{ 1049{
977 int ret; 1050 int ret;
978 1051
979 ret = pwrdm_wait_transition(pwrdm); 1052 pwrdm_lock(pwrdm);
980 if (!ret) 1053 ret = pwrdm_state_switch_nolock(pwrdm);
981 ret = _pwrdm_state_switch(pwrdm, PWRDM_STATE_NOW); 1054 pwrdm_unlock(pwrdm);
982 1055
983 return ret; 1056 return ret;
984} 1057}
@@ -1004,6 +1077,61 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
1004} 1077}
1005 1078
1006/** 1079/**
1080 * omap_set_pwrdm_state - change a powerdomain's current power state
1081 * @pwrdm: struct powerdomain * to change the power state of
1082 * @pwrst: power state to change to
1083 *
1084 * Change the current hardware power state of the powerdomain
1085 * represented by @pwrdm to the power state represented by @pwrst.
1086 * Returns -EINVAL if @pwrdm is null or invalid or if the
1087 * powerdomain's current power state could not be read, or returns 0
1088 * upon success or if @pwrdm does not support @pwrst or any
1089 * lower-power state. XXX Should not return 0 if the @pwrdm does not
1090 * support @pwrst or any lower-power state: this should be an error.
1091 */
1092int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
1093{
1094 u8 curr_pwrst, next_pwrst, sleep_switch;
1095 int ret = 0;
1096 bool hwsup = false;
1097
1098 if (!pwrdm || IS_ERR(pwrdm))
1099 return -EINVAL;
1100
1101 while (!(pwrdm->pwrsts & (1 << pwrst))) {
1102 if (pwrst == PWRDM_POWER_OFF)
1103 return ret;
1104 pwrst--;
1105 }
1106
1107 pwrdm_lock(pwrdm);
1108
1109 curr_pwrst = pwrdm_read_pwrst(pwrdm);
1110 next_pwrst = pwrdm_read_next_pwrst(pwrdm);
1111 if (curr_pwrst == pwrst && next_pwrst == pwrst)
1112 goto osps_out;
1113
1114 sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
1115 pwrst, &hwsup);
1116 if (sleep_switch == ERROR_SWITCH) {
1117 ret = -EINVAL;
1118 goto osps_out;
1119 }
1120
1121 ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
1122 if (ret)
1123 pr_err("%s: unable to set power state of powerdomain: %s\n",
1124 __func__, pwrdm->name);
1125
1126 _pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
1127
1128osps_out:
1129 pwrdm_unlock(pwrdm);
1130
1131 return ret;
1132}
1133
1134/**
1007 * pwrdm_get_context_loss_count - get powerdomain's context loss count 1135 * pwrdm_get_context_loss_count - get powerdomain's context loss count
1008 * @pwrdm: struct powerdomain * to wait for 1136 * @pwrdm: struct powerdomain * to wait for
1009 * 1137 *