aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-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);