debuggers.hg

view xen/arch/x86/acpi/power.c @ 17943:baaea9f0db5e

x86: Add cpufreq logic to S3 suspend/resume

When suspend to S3, stop the cpufreq dbs governor. When resume from
S3, firstly sync cpu state and freq at the 1st dbs timer; from 2nd dbs
timer on, cpufreq dbs governor control cpu px transfer according to
its workload algorithm. Px statistic is also handled.

Signed-off-by: Liu Jinsong <jinsong.liu@intel.com>
author Keir Fraser <keir.fraser@citrix.com>
date Fri Jun 27 16:16:47 2008 +0100 (2008-06-27)
parents ad156e312aef
children c503269192f2
line source
1 /* drivers/acpi/sleep/power.c - PM core functionality for Xen
2 *
3 * Copyrights from Linux side:
4 * Copyright (c) 2000-2003 Patrick Mochel
5 * Copyright (C) 2001-2003 Pavel Machek <pavel@suse.cz>
6 * Copyright (c) 2003 Open Source Development Lab
7 * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com>
8 * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com>
9 *
10 * Slimmed with Xen specific support.
11 */
13 #include <xen/config.h>
14 #include <asm/io.h>
15 #include <asm/acpi.h>
16 #include <xen/acpi.h>
17 #include <xen/errno.h>
18 #include <xen/iocap.h>
19 #include <xen/sched.h>
20 #include <asm/acpi.h>
21 #include <asm/irq.h>
22 #include <asm/init.h>
23 #include <xen/spinlock.h>
24 #include <xen/sched.h>
25 #include <xen/domain.h>
26 #include <xen/console.h>
27 #include <public/platform.h>
28 #include <asm/tboot.h>
30 #include <acpi/cpufreq/cpufreq.h>
32 static char opt_acpi_sleep[20];
33 string_param("acpi_sleep", opt_acpi_sleep);
35 static u8 sleep_states[ACPI_S_STATE_COUNT];
36 static DEFINE_SPINLOCK(pm_lock);
38 struct acpi_sleep_info acpi_sinfo;
40 void do_suspend_lowlevel(void);
42 static int device_power_down(void)
43 {
44 console_suspend();
46 time_suspend();
48 i8259A_suspend();
50 ioapic_suspend();
52 lapic_suspend();
54 return 0;
55 }
57 static void device_power_up(void)
58 {
59 lapic_resume();
61 ioapic_resume();
63 i8259A_resume();
65 time_resume();
67 console_resume();
68 }
70 static void freeze_domains(void)
71 {
72 struct domain *d;
74 for_each_domain ( d )
75 if ( d->domain_id != 0 )
76 domain_pause(d);
77 }
79 static void thaw_domains(void)
80 {
81 struct domain *d;
83 for_each_domain ( d )
84 if ( d->domain_id != 0 )
85 domain_unpause(d);
86 }
88 static void acpi_sleep_prepare(u32 state)
89 {
90 void *wakeup_vector_va;
92 if ( state != ACPI_STATE_S3 )
93 return;
95 wakeup_vector_va = __acpi_map_table(
96 acpi_sinfo.wakeup_vector, sizeof(uint64_t));
97 if ( acpi_sinfo.vector_width == 32 )
98 {
99 *(uint32_t *)wakeup_vector_va =
100 tboot_in_measured_env() ?
101 (uint32_t)g_tboot_shared->s3_tb_wakeup_entry :
102 (uint32_t)bootsym_phys(wakeup_start);
103 }
104 else
105 {
106 *(uint64_t *)wakeup_vector_va =
107 tboot_in_measured_env() ?
108 (uint64_t)g_tboot_shared->s3_tb_wakeup_entry :
109 (uint64_t)bootsym_phys(wakeup_start);
110 }
111 }
113 static void acpi_sleep_post(u32 state) {}
115 /* Main interface to do xen specific suspend/resume */
116 static int enter_state(u32 state)
117 {
118 unsigned long flags;
119 int error;
121 if ( (state <= ACPI_STATE_S0) || (state > ACPI_S_STATES_MAX) )
122 return -EINVAL;
124 if ( !spin_trylock(&pm_lock) )
125 return -EBUSY;
127 printk(XENLOG_INFO "Preparing system for ACPI S%d state.", state);
129 freeze_domains();
131 cpufreq_suspend();
133 disable_nonboot_cpus();
134 if ( num_online_cpus() != 1 )
135 {
136 error = -EBUSY;
137 goto enable_cpu;
138 }
140 hvm_cpu_down();
142 acpi_sleep_prepare(state);
144 console_start_sync();
145 printk("Entering ACPI S%d state.\n", state);
147 local_irq_save(flags);
149 if ( (error = device_power_down()) )
150 {
151 printk(XENLOG_ERR "Some devices failed to power down.");
152 goto done;
153 }
155 ACPI_FLUSH_CPU_CACHE();
157 switch ( state )
158 {
159 case ACPI_STATE_S3:
160 do_suspend_lowlevel();
161 break;
162 case ACPI_STATE_S5:
163 acpi_enter_sleep_state(ACPI_STATE_S5);
164 break;
165 default:
166 error = -EINVAL;
167 break;
168 }
170 /* Restore CR4 and EFER from cached values. */
171 write_cr4(read_cr4());
172 if ( cpu_has_efer )
173 write_efer(read_efer());
175 device_power_up();
177 printk(XENLOG_INFO "Finishing wakeup from ACPI S%d state.", state);
179 done:
180 local_irq_restore(flags);
181 console_end_sync();
182 acpi_sleep_post(state);
183 if ( !hvm_cpu_up() )
184 BUG();
186 enable_cpu:
187 enable_nonboot_cpus();
188 cpufreq_resume();
189 thaw_domains();
190 spin_unlock(&pm_lock);
191 return error;
192 }
194 static long enter_state_helper(void *data)
195 {
196 struct acpi_sleep_info *sinfo = (struct acpi_sleep_info *)data;
197 return enter_state(sinfo->sleep_state);
198 }
200 /*
201 * Dom0 issues this hypercall in place of writing pm1a_cnt. Xen then
202 * takes over the control and put the system into sleep state really.
203 */
204 int acpi_enter_sleep(struct xenpf_enter_acpi_sleep *sleep)
205 {
206 if ( !IS_PRIV(current->domain) || !acpi_sinfo.pm1a_cnt_blk.address )
207 return -EPERM;
209 /* Sanity check */
210 if ( acpi_sinfo.pm1b_cnt_val &&
211 ((sleep->pm1a_cnt_val ^ sleep->pm1b_cnt_val) &
212 ACPI_BITMASK_SLEEP_ENABLE) )
213 {
214 gdprintk(XENLOG_ERR, "Mismatched pm1a/pm1b setting.");
215 return -EINVAL;
216 }
218 if ( sleep->flags )
219 return -EINVAL;
221 acpi_sinfo.pm1a_cnt_val = sleep->pm1a_cnt_val;
222 acpi_sinfo.pm1b_cnt_val = sleep->pm1b_cnt_val;
223 acpi_sinfo.sleep_state = sleep->sleep_state;
225 return continue_hypercall_on_cpu(0, enter_state_helper, &acpi_sinfo);
226 }
228 static int acpi_get_wake_status(void)
229 {
230 uint32_t val;
231 acpi_status status;
233 /* Wake status is the 15th bit of PM1 status register. (ACPI spec 3.0) */
234 status = acpi_hw_register_read(ACPI_REGISTER_PM1_STATUS, &val);
235 if ( ACPI_FAILURE(status) )
236 return 0;
238 val &= ACPI_BITMASK_WAKE_STATUS;
239 val >>= ACPI_BITPOSITION_WAKE_STATUS;
240 return val;
241 }
243 static void tboot_sleep(u8 sleep_state)
244 {
245 uint32_t shutdown_type;
247 g_tboot_shared->acpi_sinfo.pm1a_cnt =
248 (uint16_t)acpi_sinfo.pm1a_cnt_blk.address;
249 g_tboot_shared->acpi_sinfo.pm1b_cnt =
250 (uint16_t)acpi_sinfo.pm1b_cnt_blk.address;
251 g_tboot_shared->acpi_sinfo.pm1a_evt =
252 (uint16_t)acpi_sinfo.pm1a_evt_blk.address;
253 g_tboot_shared->acpi_sinfo.pm1b_evt =
254 (uint16_t)acpi_sinfo.pm1b_evt_blk.address;
255 g_tboot_shared->acpi_sinfo.pm1a_cnt_val = acpi_sinfo.pm1a_cnt_val;
256 g_tboot_shared->acpi_sinfo.pm1b_cnt_val = acpi_sinfo.pm1b_cnt_val;
258 switch ( sleep_state )
259 {
260 case ACPI_STATE_S3:
261 shutdown_type = TB_SHUTDOWN_S3;
262 g_tboot_shared->s3_k_wakeup_entry =
263 (uint32_t)bootsym_phys(wakeup_start);
264 break;
265 case ACPI_STATE_S4:
266 shutdown_type = TB_SHUTDOWN_S4;
267 break;
268 case ACPI_STATE_S5:
269 shutdown_type = TB_SHUTDOWN_S5;
270 break;
271 default:
272 return;
273 }
275 tboot_shutdown(shutdown_type);
276 }
278 /* System is really put into sleep state by this stub */
279 acpi_status asmlinkage acpi_enter_sleep_state(u8 sleep_state)
280 {
281 acpi_status status;
283 if ( tboot_in_measured_env() )
284 {
285 tboot_sleep(sleep_state);
286 printk(XENLOG_ERR "TBOOT failed entering s3 state\n");
287 return_ACPI_STATUS(AE_ERROR);
288 }
290 ACPI_FLUSH_CPU_CACHE();
292 status = acpi_hw_register_write(ACPI_REGISTER_PM1A_CONTROL,
293 acpi_sinfo.pm1a_cnt_val);
294 if ( ACPI_FAILURE(status) )
295 return_ACPI_STATUS(AE_ERROR);
297 if ( acpi_sinfo.pm1b_cnt_blk.address )
298 {
299 status = acpi_hw_register_write(ACPI_REGISTER_PM1B_CONTROL,
300 acpi_sinfo.pm1b_cnt_val);
301 if ( ACPI_FAILURE(status) )
302 return_ACPI_STATUS(AE_ERROR);
303 }
305 /* Wait until we enter sleep state, and spin until we wake */
306 while ( !acpi_get_wake_status() )
307 continue;
309 return_ACPI_STATUS(AE_OK);
310 }
312 static int __init acpi_sleep_init(void)
313 {
314 int i;
315 char *p = opt_acpi_sleep;
317 while ( (p != NULL) && (*p != '\0') )
318 {
319 if ( !strncmp(p, "s3_bios", 7) )
320 acpi_video_flags |= 1;
321 if ( !strncmp(p, "s3_mode", 7) )
322 acpi_video_flags |= 2;
323 p = strchr(p, ',');
324 if ( p != NULL )
325 p += strspn(p, ", \t");
326 }
328 printk(XENLOG_INFO "ACPI sleep modes:");
329 for ( i = 0; i < ACPI_S_STATE_COUNT; i++ )
330 {
331 if ( i == ACPI_STATE_S3 )
332 {
333 sleep_states[i] = 1;
334 printk(" S%d", i);
335 }
336 else
337 sleep_states[i] = 0;
338 }
339 printk("\n");
341 return 0;
342 }
343 __initcall(acpi_sleep_init);