Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

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