aboutsummaryrefslogtreecommitdiffstats
path: root/arch/arm/mach-omap2
diff options
context:
space:
mode:
authorPaul Walmsley <paul@pwsan.com>2013-01-29 15:45:09 -0500
committerPaul Walmsley <paul@pwsan.com>2013-01-29 16:59:57 -0500
commitc4978fba6b2d4e3a584d72c067a371871fecbedc (patch)
tree75add16022eb4e71ae50e1aecb42d1d5a9f420ab /arch/arm/mach-omap2
parentf8457c2d8be94779c8c460060e536d3a2a02c8d8 (diff)
ARM: OMAP2+: PM/powerdomain: move omap_set_pwrdm_state() to powerdomain code
Move omap_set_pwrdm_state() from the PM code to the powerdomain code, and refactor it to split it up into several functions. A subsequent patch will rename it to conform with the existing powerdomain function names. This version includes some additional documentation, based on a suggestion from Jean Pihet. It also modifies omap_set_pwrdm_state() to not bail out early unless both the powerdomain current power state and the next power state are equal. (Previously it would terminate early if the next power state was equal to the target power state, which was insufficiently rigorous.) Signed-off-by: Paul Walmsley <paul@pwsan.com> Cc: Jean Pihet <jean.pihet@newoldbits.com> Cc: Kevin Hilman <khilman@deeprootsystems.com> Cc: Tero Kristo <t-kristo@ti.com>
Diffstat (limited to 'arch/arm/mach-omap2')
-rw-r--r--arch/arm/mach-omap2/pm.c61
-rw-r--r--arch/arm/mach-omap2/pm.h1
-rw-r--r--arch/arm/mach-omap2/powerdomain.c161
-rw-r--r--arch/arm/mach-omap2/powerdomain.h13
4 files changed, 144 insertions, 92 deletions
diff --git a/arch/arm/mach-omap2/pm.c b/arch/arm/mach-omap2/pm.c
index f18afc9cbd8e..48d6d5d2c926 100644
--- a/arch/arm/mach-omap2/pm.c
+++ b/arch/arm/mach-omap2/pm.c
@@ -108,10 +108,6 @@ static void __init omap2_init_processor_devices(void)
108 } 108 }
109} 109}
110 110
111/* Types of sleep_switch used in omap_set_pwrdm_state */
112#define FORCEWAKEUP_SWITCH 0
113#define LOWPOWERSTATE_SWITCH 1
114
115int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) 111int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
116{ 112{
117 if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) && 113 if ((clkdm->flags & CLKDM_CAN_ENABLE_AUTO) &&
@@ -124,63 +120,6 @@ int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused)
124} 120}
125 121
126/* 122/*
127 * This sets pwrdm state (other than mpu & core. Currently only ON &
128 * RET are supported.
129 */
130int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 pwrst)
131{
132 u8 curr_pwrst, next_pwrst;
133 int sleep_switch = -1, ret = 0, hwsup = 0;
134
135 if (!pwrdm || IS_ERR(pwrdm))
136 return -EINVAL;
137
138 while (!(pwrdm->pwrsts & (1 << pwrst))) {
139 if (pwrst == PWRDM_POWER_OFF)
140 return ret;
141 pwrst--;
142 }
143
144 next_pwrst = pwrdm_read_next_pwrst(pwrdm);
145 if (next_pwrst == pwrst)
146 return ret;
147
148 curr_pwrst = pwrdm_read_pwrst(pwrdm);
149 if (curr_pwrst < PWRDM_POWER_ON) {
150 if ((curr_pwrst > pwrst) &&
151 (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE)) {
152 sleep_switch = LOWPOWERSTATE_SWITCH;
153 } else {
154 hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
155 clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
156 sleep_switch = FORCEWAKEUP_SWITCH;
157 }
158 }
159
160 ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
161 if (ret)
162 pr_err("%s: unable to set power state of powerdomain: %s\n",
163 __func__, pwrdm->name);
164
165 switch (sleep_switch) {
166 case FORCEWAKEUP_SWITCH:
167 if (hwsup)
168 clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
169 else
170 clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
171 break;
172 case LOWPOWERSTATE_SWITCH:
173 pwrdm_set_lowpwrstchange(pwrdm);
174 pwrdm_state_switch(pwrdm);
175 break;
176 }
177
178 return ret;
179}
180
181
182
183/*
184 * This API is to be called during init to set the various voltage 123 * This API is to be called during init to set the various voltage
185 * domains to the voltage as per the opp table. Typically we boot up 124 * domains to the voltage as per the opp table. Typically we boot up
186 * at the nominal voltage. So this function finds out the rate of 125 * at the nominal voltage. So this function finds out the rate of
diff --git a/arch/arm/mach-omap2/pm.h b/arch/arm/mach-omap2/pm.h
index c22503b17abd..7bdd22afce69 100644
--- a/arch/arm/mach-omap2/pm.h
+++ b/arch/arm/mach-omap2/pm.h
@@ -33,7 +33,6 @@ static inline int omap4_idle_init(void)
33extern void *omap3_secure_ram_storage; 33extern void *omap3_secure_ram_storage;
34extern void omap3_pm_off_mode_enable(int); 34extern void omap3_pm_off_mode_enable(int);
35extern void omap_sram_idle(void); 35extern void omap_sram_idle(void);
36extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state);
37extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); 36extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused);
38extern int (*omap_pm_suspend)(void); 37extern int (*omap_pm_suspend)(void);
39 38
diff --git a/arch/arm/mach-omap2/powerdomain.c b/arch/arm/mach-omap2/powerdomain.c
index 97b3881dd60d..65c34645c8ed 100644
--- a/arch/arm/mach-omap2/powerdomain.c
+++ b/arch/arm/mach-omap2/powerdomain.c
@@ -42,6 +42,16 @@ enum {
42 PWRDM_STATE_PREV, 42 PWRDM_STATE_PREV,
43}; 43};
44 44
45/*
46 * Types of sleep_switch used internally in omap_set_pwrdm_state()
47 * and its associated static functions
48 *
49 * XXX Better documentation is needed here
50 */
51#define ALREADYACTIVE_SWITCH 0
52#define FORCEWAKEUP_SWITCH 1
53#define LOWPOWERSTATE_SWITCH 2
54#define ERROR_SWITCH 3
45 55
46/* pwrdm_list contains all registered struct powerdomains */ 56/* pwrdm_list contains all registered struct powerdomains */
47static LIST_HEAD(pwrdm_list); 57static LIST_HEAD(pwrdm_list);
@@ -200,6 +210,80 @@ static int _pwrdm_post_transition_cb(struct powerdomain *pwrdm, void *unused)
200 return 0; 210 return 0;
201} 211}
202 212
213/**
214 * _pwrdm_save_clkdm_state_and_activate - prepare for power state change
215 * @pwrdm: struct powerdomain * to operate on
216 * @curr_pwrst: current power state of @pwrdm
217 * @pwrst: power state to switch to
218 * @hwsup: ptr to a bool to return whether the clkdm is hardware-supervised
219 *
220 * Determine whether the powerdomain needs to be turned on before
221 * attempting to switch power states. Called by
222 * omap_set_pwrdm_state(). NOTE that if the powerdomain contains
223 * multiple clockdomains, this code assumes that the first clockdomain
224 * supports software-supervised wakeup mode - potentially a problem.
225 * Returns the power state switch mode currently in use (see the
226 * "Types of sleep_switch" comment above).
227 */
228static u8 _pwrdm_save_clkdm_state_and_activate(struct powerdomain *pwrdm,
229 u8 curr_pwrst, u8 pwrst,
230 bool *hwsup)
231{
232 u8 sleep_switch;
233
234 if (curr_pwrst < 0) {
235 WARN_ON(1);
236 sleep_switch = ERROR_SWITCH;
237 } else if (curr_pwrst < PWRDM_POWER_ON) {
238 if (curr_pwrst > pwrst &&
239 pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
240 arch_pwrdm->pwrdm_set_lowpwrstchange) {
241 sleep_switch = LOWPOWERSTATE_SWITCH;
242 } else {
243 *hwsup = clkdm_in_hwsup(pwrdm->pwrdm_clkdms[0]);
244 clkdm_wakeup(pwrdm->pwrdm_clkdms[0]);
245 sleep_switch = FORCEWAKEUP_SWITCH;
246 }
247 } else {
248 sleep_switch = ALREADYACTIVE_SWITCH;
249 }
250
251 return sleep_switch;
252}
253
254/**
255 * _pwrdm_restore_clkdm_state - restore the clkdm hwsup state after pwrst change
256 * @pwrdm: struct powerdomain * to operate on
257 * @sleep_switch: return value from _pwrdm_save_clkdm_state_and_activate()
258 * @hwsup: should @pwrdm's first clockdomain be set to hardware-supervised mode?
259 *
260 * Restore the clockdomain state perturbed by
261 * _pwrdm_save_clkdm_state_and_activate(), and call the power state
262 * bookkeeping code. Called by omap_set_pwrdm_state(). NOTE that if
263 * the powerdomain contains multiple clockdomains, this assumes that
264 * the first associated clockdomain supports either
265 * hardware-supervised idle control in the register, or
266 * software-supervised sleep. No return value.
267 */
268static void _pwrdm_restore_clkdm_state(struct powerdomain *pwrdm,
269 u8 sleep_switch, bool hwsup)
270{
271 switch (sleep_switch) {
272 case FORCEWAKEUP_SWITCH:
273 if (hwsup)
274 clkdm_allow_idle(pwrdm->pwrdm_clkdms[0]);
275 else
276 clkdm_sleep(pwrdm->pwrdm_clkdms[0]);
277 break;
278 case LOWPOWERSTATE_SWITCH:
279 if (pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE &&
280 arch_pwrdm->pwrdm_set_lowpwrstchange)
281 arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
282 pwrdm_state_switch(pwrdm);
283 break;
284 }
285}
286
203/* Public functions */ 287/* Public functions */
204 288
205/** 289/**
@@ -921,35 +1005,6 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm)
921 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0; 1005 return (pwrdm && pwrdm->flags & PWRDM_HAS_HDWR_SAR) ? 1 : 0;
922} 1006}
923 1007
924/**
925 * pwrdm_set_lowpwrstchange - Request a low power state change
926 * @pwrdm: struct powerdomain *
927 *
928 * Allows a powerdomain to transtion to a lower power sleep state
929 * from an existing sleep state without waking up the powerdomain.
930 * Returns -EINVAL if the powerdomain pointer is null or if the
931 * powerdomain does not support LOWPOWERSTATECHANGE, or returns 0
932 * upon success.
933 */
934int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm)
935{
936 int ret = -EINVAL;
937
938 if (!pwrdm)
939 return -EINVAL;
940
941 if (!(pwrdm->flags & PWRDM_HAS_LOWPOWERSTATECHANGE))
942 return -EINVAL;
943
944 pr_debug("powerdomain: %s: setting LOWPOWERSTATECHANGE bit\n",
945 pwrdm->name);
946
947 if (arch_pwrdm && arch_pwrdm->pwrdm_set_lowpwrstchange)
948 ret = arch_pwrdm->pwrdm_set_lowpwrstchange(pwrdm);
949
950 return ret;
951}
952
953int pwrdm_state_switch(struct powerdomain *pwrdm) 1008int pwrdm_state_switch(struct powerdomain *pwrdm)
954{ 1009{
955 int ret; 1010 int ret;
@@ -985,6 +1040,54 @@ int pwrdm_post_transition(struct powerdomain *pwrdm)
985} 1040}
986 1041
987/** 1042/**
1043 * omap_set_pwrdm_state - change a powerdomain's current power state
1044 * @pwrdm: struct powerdomain * to change the power state of
1045 * @pwrst: power state to change to
1046 *
1047 * Change the current hardware power state of the powerdomain
1048 * represented by @pwrdm to the power state represented by @pwrst.
1049 * Returns -EINVAL if @pwrdm is null or invalid or if the
1050 * powerdomain's current power state could not be read, or returns 0
1051 * upon success or if @pwrdm does not support @pwrst or any
1052 * lower-power state. XXX Should not return 0 if the @pwrdm does not
1053 * support @pwrst or any lower-power state: this should be an error.
1054 */
1055int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 pwrst)
1056{
1057 u8 curr_pwrst, next_pwrst, sleep_switch;
1058 int ret = 0;
1059 bool hwsup = false;
1060
1061 if (!pwrdm || IS_ERR(pwrdm))
1062 return -EINVAL;
1063
1064 while (!(pwrdm->pwrsts & (1 << pwrst))) {
1065 if (pwrst == PWRDM_POWER_OFF)
1066 return ret;
1067 pwrst--;
1068 }
1069
1070 curr_pwrst = pwrdm_read_pwrst(pwrdm);
1071 next_pwrst = pwrdm_read_next_pwrst(pwrdm);
1072 if (curr_pwrst == pwrst && next_pwrst == pwrst)
1073 return ret;
1074
1075 sleep_switch = _pwrdm_save_clkdm_state_and_activate(pwrdm, curr_pwrst,
1076 pwrst, &hwsup);
1077 if (sleep_switch == ERROR_SWITCH)
1078 return -EINVAL;
1079
1080 ret = pwrdm_set_next_pwrst(pwrdm, pwrst);
1081 if (ret)
1082 pr_err("%s: unable to set power state of powerdomain: %s\n",
1083 __func__, pwrdm->name);
1084
1085 _pwrdm_restore_clkdm_state(pwrdm, sleep_switch, hwsup);
1086
1087 return ret;
1088}
1089
1090/**
988 * pwrdm_get_context_loss_count - get powerdomain's context loss count 1091 * pwrdm_get_context_loss_count - get powerdomain's context loss count
989 * @pwrdm: struct powerdomain * to wait for 1092 * @pwrdm: struct powerdomain * to wait for
990 * 1093 *
diff --git a/arch/arm/mach-omap2/powerdomain.h b/arch/arm/mach-omap2/powerdomain.h
index 7c1534bb16e9..93e7df824650 100644
--- a/arch/arm/mach-omap2/powerdomain.h
+++ b/arch/arm/mach-omap2/powerdomain.h
@@ -162,6 +162,16 @@ struct powerdomain {
162 * @pwrdm_disable_hdwr_sar: Disable Hardware Save-Restore feature for a pd 162 * @pwrdm_disable_hdwr_sar: Disable Hardware Save-Restore feature for a pd
163 * @pwrdm_set_lowpwrstchange: Enable pd transitions from a shallow to deep sleep 163 * @pwrdm_set_lowpwrstchange: Enable pd transitions from a shallow to deep sleep
164 * @pwrdm_wait_transition: Wait for a pd state transition to complete 164 * @pwrdm_wait_transition: Wait for a pd state transition to complete
165 *
166 * Regarding @pwrdm_set_lowpwrstchange: On the OMAP2 and 3-family
167 * chips, a powerdomain's power state is not allowed to directly
168 * transition from one low-power state (e.g., CSWR) to another
169 * low-power state (e.g., OFF) without first waking up the
170 * powerdomain. This wastes energy. So OMAP4 chips support the
171 * ability to transition a powerdomain power state directly from one
172 * low-power state to another. The function pointed to by
173 * @pwrdm_set_lowpwrstchange is intended to configure the OMAP4
174 * hardware powerdomain state machine to enable this feature.
165 */ 175 */
166struct pwrdm_ops { 176struct pwrdm_ops {
167 int (*pwrdm_set_next_pwrst)(struct powerdomain *pwrdm, u8 pwrst); 177 int (*pwrdm_set_next_pwrst)(struct powerdomain *pwrdm, u8 pwrst);
@@ -228,10 +238,11 @@ bool pwrdm_has_hdwr_sar(struct powerdomain *pwrdm);
228int pwrdm_state_switch(struct powerdomain *pwrdm); 238int pwrdm_state_switch(struct powerdomain *pwrdm);
229int pwrdm_pre_transition(struct powerdomain *pwrdm); 239int pwrdm_pre_transition(struct powerdomain *pwrdm);
230int pwrdm_post_transition(struct powerdomain *pwrdm); 240int pwrdm_post_transition(struct powerdomain *pwrdm);
231int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm);
232int pwrdm_get_context_loss_count(struct powerdomain *pwrdm); 241int pwrdm_get_context_loss_count(struct powerdomain *pwrdm);
233bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm); 242bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm);
234 243
244extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 state);
245
235extern void omap242x_powerdomains_init(void); 246extern void omap242x_powerdomains_init(void);
236extern void omap243x_powerdomains_init(void); 247extern void omap243x_powerdomains_init(void);
237extern void omap3xxx_powerdomains_init(void); 248extern void omap3xxx_powerdomains_init(void);