Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux
at v2.6.20-rc2 232 lines 5.5 kB view raw
1/* 2 * sleep.c - ACPI sleep support. 3 * 4 * Copyright (c) 2005 Alexey Starikovskiy <alexey.y.starikovskiy@intel.com> 5 * Copyright (c) 2004 David Shaohua Li <shaohua.li@intel.com> 6 * Copyright (c) 2000-2003 Patrick Mochel 7 * Copyright (c) 2003 Open Source Development Lab 8 * 9 * This file is released under the GPLv2. 10 * 11 */ 12 13#include <linux/delay.h> 14#include <linux/irq.h> 15#include <linux/dmi.h> 16#include <linux/device.h> 17#include <linux/suspend.h> 18#include <acpi/acpi_bus.h> 19#include <acpi/acpi_drivers.h> 20#include "sleep.h" 21 22u8 sleep_states[ACPI_S_STATE_COUNT]; 23 24static struct pm_ops acpi_pm_ops; 25 26extern void do_suspend_lowlevel(void); 27 28static u32 acpi_suspend_states[] = { 29 [PM_SUSPEND_ON] = ACPI_STATE_S0, 30 [PM_SUSPEND_STANDBY] = ACPI_STATE_S1, 31 [PM_SUSPEND_MEM] = ACPI_STATE_S3, 32 [PM_SUSPEND_DISK] = ACPI_STATE_S4, 33 [PM_SUSPEND_MAX] = ACPI_STATE_S5 34}; 35 36static int init_8259A_after_S1; 37 38/** 39 * acpi_pm_prepare - Do preliminary suspend work. 40 * @pm_state: suspend state we're entering. 41 * 42 * Make sure we support the state. If we do, and we need it, set the 43 * firmware waking vector and do arch-specific nastiness to get the 44 * wakeup code to the waking vector. 45 */ 46 47extern int acpi_sleep_prepare(u32 acpi_state); 48extern void acpi_power_off(void); 49 50static int acpi_pm_prepare(suspend_state_t pm_state) 51{ 52 u32 acpi_state = acpi_suspend_states[pm_state]; 53 54 if (!sleep_states[acpi_state]) { 55 printk("acpi_pm_prepare does not support %d \n", pm_state); 56 return -EPERM; 57 } 58 return acpi_sleep_prepare(acpi_state); 59} 60 61/** 62 * acpi_pm_enter - Actually enter a sleep state. 63 * @pm_state: State we're entering. 64 * 65 * Flush caches and go to sleep. For STR or STD, we have to call 66 * arch-specific assembly, which in turn call acpi_enter_sleep_state(). 67 * It's unfortunate, but it works. Please fix if you're feeling frisky. 68 */ 69 70static int acpi_pm_enter(suspend_state_t pm_state) 71{ 72 acpi_status status = AE_OK; 73 unsigned long flags = 0; 74 u32 acpi_state = acpi_suspend_states[pm_state]; 75 76 ACPI_FLUSH_CPU_CACHE(); 77 78 /* Do arch specific saving of state. */ 79 if (pm_state > PM_SUSPEND_STANDBY) { 80 int error = acpi_save_state_mem(); 81 if (error) 82 return error; 83 } 84 85 local_irq_save(flags); 86 acpi_enable_wakeup_device(acpi_state); 87 switch (pm_state) { 88 case PM_SUSPEND_STANDBY: 89 barrier(); 90 status = acpi_enter_sleep_state(acpi_state); 91 break; 92 93 case PM_SUSPEND_MEM: 94 do_suspend_lowlevel(); 95 break; 96 97 case PM_SUSPEND_DISK: 98 if (acpi_pm_ops.pm_disk_mode == PM_DISK_PLATFORM) 99 status = acpi_enter_sleep_state(acpi_state); 100 break; 101 case PM_SUSPEND_MAX: 102 acpi_power_off(); 103 break; 104 105 default: 106 return -EINVAL; 107 } 108 109 /* ACPI 3.0 specs (P62) says that it's the responsabilty 110 * of the OSPM to clear the status bit [ implying that the 111 * POWER_BUTTON event should not reach userspace ] 112 */ 113 if (ACPI_SUCCESS(status) && (acpi_state == ACPI_STATE_S3)) 114 acpi_clear_event(ACPI_EVENT_POWER_BUTTON); 115 116 local_irq_restore(flags); 117 printk(KERN_DEBUG "Back to C!\n"); 118 119 /* restore processor state 120 * We should only be here if we're coming back from STR or STD. 121 * And, in the case of the latter, the memory image should have already 122 * been loaded from disk. 123 */ 124 if (pm_state > PM_SUSPEND_STANDBY) 125 acpi_restore_state_mem(); 126 127 return ACPI_SUCCESS(status) ? 0 : -EFAULT; 128} 129 130/** 131 * acpi_pm_finish - Finish up suspend sequence. 132 * @pm_state: State we're coming out of. 133 * 134 * This is called after we wake back up (or if entering the sleep state 135 * failed). 136 */ 137 138static int acpi_pm_finish(suspend_state_t pm_state) 139{ 140 u32 acpi_state = acpi_suspend_states[pm_state]; 141 142 acpi_leave_sleep_state(acpi_state); 143 acpi_disable_wakeup_device(acpi_state); 144 145 /* reset firmware waking vector */ 146 acpi_set_firmware_waking_vector((acpi_physical_address) 0); 147 148 if (init_8259A_after_S1) { 149 printk("Broken toshiba laptop -> kicking interrupts\n"); 150 init_8259A(0); 151 } 152 return 0; 153} 154 155int acpi_suspend(u32 acpi_state) 156{ 157 suspend_state_t states[] = { 158 [1] = PM_SUSPEND_STANDBY, 159 [3] = PM_SUSPEND_MEM, 160 [4] = PM_SUSPEND_DISK, 161 [5] = PM_SUSPEND_MAX 162 }; 163 164 if (acpi_state < 6 && states[acpi_state]) 165 return pm_suspend(states[acpi_state]); 166 return -EINVAL; 167} 168 169static int acpi_pm_state_valid(suspend_state_t pm_state) 170{ 171 u32 acpi_state = acpi_suspend_states[pm_state]; 172 173 return sleep_states[acpi_state]; 174} 175 176static struct pm_ops acpi_pm_ops = { 177 .valid = acpi_pm_state_valid, 178 .prepare = acpi_pm_prepare, 179 .enter = acpi_pm_enter, 180 .finish = acpi_pm_finish, 181}; 182 183/* 184 * Toshiba fails to preserve interrupts over S1, reinitialization 185 * of 8259 is needed after S1 resume. 186 */ 187static int __init init_ints_after_s1(struct dmi_system_id *d) 188{ 189 printk(KERN_WARNING "%s with broken S1 detected.\n", d->ident); 190 init_8259A_after_S1 = 1; 191 return 0; 192} 193 194static struct dmi_system_id __initdata acpisleep_dmi_table[] = { 195 { 196 .callback = init_ints_after_s1, 197 .ident = "Toshiba Satellite 4030cdt", 198 .matches = {DMI_MATCH(DMI_PRODUCT_NAME, "S4030CDT/4.3"),}, 199 }, 200 {}, 201}; 202 203static int __init acpi_sleep_init(void) 204{ 205 int i = 0; 206 207 dmi_check_system(acpisleep_dmi_table); 208 209 if (acpi_disabled) 210 return 0; 211 212 printk(KERN_INFO PREFIX "(supports"); 213 for (i = 0; i < ACPI_S_STATE_COUNT; i++) { 214 acpi_status status; 215 u8 type_a, type_b; 216 status = acpi_get_sleep_type_data(i, &type_a, &type_b); 217 if (ACPI_SUCCESS(status)) { 218 sleep_states[i] = 1; 219 printk(" S%d", i); 220 } 221 if (i == ACPI_STATE_S4) { 222 if (sleep_states[i]) 223 acpi_pm_ops.pm_disk_mode = PM_DISK_PLATFORM; 224 } 225 } 226 printk(")\n"); 227 228 pm_set_ops(&acpi_pm_ops); 229 return 0; 230} 231 232late_initcall(acpi_sleep_init);