diff options
-rw-r--r-- | arch/arm/mach-omap2/pm.c | 61 | ||||
-rw-r--r-- | arch/arm/mach-omap2/pm.h | 1 | ||||
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.c | 161 | ||||
-rw-r--r-- | arch/arm/mach-omap2/powerdomain.h | 13 |
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 | |||
115 | int __init omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused) | 111 | int __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 | */ | ||
130 | int 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) | |||
33 | extern void *omap3_secure_ram_storage; | 33 | extern void *omap3_secure_ram_storage; |
34 | extern void omap3_pm_off_mode_enable(int); | 34 | extern void omap3_pm_off_mode_enable(int); |
35 | extern void omap_sram_idle(void); | 35 | extern void omap_sram_idle(void); |
36 | extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u32 state); | ||
37 | extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); | 36 | extern int omap_pm_clkdms_setup(struct clockdomain *clkdm, void *unused); |
38 | extern int (*omap_pm_suspend)(void); | 37 | extern 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 */ |
47 | static LIST_HEAD(pwrdm_list); | 57 | static 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 | */ | ||
228 | static 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 | */ | ||
268 | static 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 | */ | ||
934 | int 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 | |||
953 | int pwrdm_state_switch(struct powerdomain *pwrdm) | 1008 | int 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 | */ | ||
1055 | int 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 | */ |
166 | struct pwrdm_ops { | 176 | struct 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); | |||
228 | int pwrdm_state_switch(struct powerdomain *pwrdm); | 238 | int pwrdm_state_switch(struct powerdomain *pwrdm); |
229 | int pwrdm_pre_transition(struct powerdomain *pwrdm); | 239 | int pwrdm_pre_transition(struct powerdomain *pwrdm); |
230 | int pwrdm_post_transition(struct powerdomain *pwrdm); | 240 | int pwrdm_post_transition(struct powerdomain *pwrdm); |
231 | int pwrdm_set_lowpwrstchange(struct powerdomain *pwrdm); | ||
232 | int pwrdm_get_context_loss_count(struct powerdomain *pwrdm); | 241 | int pwrdm_get_context_loss_count(struct powerdomain *pwrdm); |
233 | bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm); | 242 | bool pwrdm_can_ever_lose_context(struct powerdomain *pwrdm); |
234 | 243 | ||
244 | extern int omap_set_pwrdm_state(struct powerdomain *pwrdm, u8 state); | ||
245 | |||
235 | extern void omap242x_powerdomains_init(void); | 246 | extern void omap242x_powerdomains_init(void); |
236 | extern void omap243x_powerdomains_init(void); | 247 | extern void omap243x_powerdomains_init(void); |
237 | extern void omap3xxx_powerdomains_init(void); | 248 | extern void omap3xxx_powerdomains_init(void); |