commented early_printk patch because of rejects.
[linux-flexiantxendom0-3.2.10.git] / drivers / base / power.c
1 /*
2  * power.c - power management functions for the device tree.
3  * 
4  * Copyright (c) 2002-3 Patrick Mochel
5  *               2002-3 Open Source Development Lab
6  * 
7  * This file is released under the GPLv2
8  * 
9  *  Kai Germaschewski contributed to the list walking routines.
10  *
11  */
12
13 #undef DEBUG
14
15 #include <linux/device.h>
16 #include <linux/module.h>
17 #include <asm/semaphore.h>
18 #include "base.h"
19
20 #define to_dev(node) container_of(node,struct device,kobj.entry)
21
22 extern struct subsystem devices_subsys;
23
24 /**
25  * We handle system devices differently - we suspend and shut them 
26  * down first and resume them first. That way, we do anything stupid like
27  * shutting down the interrupt controller before any devices..
28  *
29  * Note that there are not different stages for power management calls - 
30  * they only get one called once when interrupts are disabled. 
31  */
32
33 extern int sysdev_shutdown(void);
34 extern int sysdev_save(u32 state);
35 extern int sysdev_suspend(u32 state);
36 extern int sysdev_resume(void);
37 extern int sysdev_restore(void);
38
39 /**
40  * device_suspend - suspend/remove all devices on the device ree
41  * @state:      state we're entering
42  * @level:      what stage of the suspend process we're at
43  *    (emb: it seems that these two arguments are described backwards of what
44  *          they actually mean .. is this correct?)
45  *
46  * The entries in the global device list are inserted such that they're in a
47  * depth-first ordering.  So, simply interate over the list, and call the 
48  * driver's suspend or remove callback for each device.
49  */
50 int device_suspend(u32 state, u32 level)
51 {
52         struct device * dev;
53         int error = 0;
54
55         down_write(&devices_subsys.rwsem);
56         list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
57                 if (dev->driver && dev->driver->suspend) {
58                         pr_debug("suspending device %s\n",dev->name);
59                         error = dev->driver->suspend(dev,state,level);
60                         if (error)
61                                 printk(KERN_ERR "%s: suspend returned %d\n",
62                                        dev->name,error);
63                 }
64         }
65         up_write(&devices_subsys.rwsem);
66
67         /*
68          * Make sure system devices are suspended.
69          */
70         switch(level) {
71         case SUSPEND_SAVE_STATE:
72                 sysdev_save(state);
73                 break;
74         case SUSPEND_POWER_DOWN:
75                 sysdev_suspend(state);
76                 break;
77         default:
78                 break;
79         }
80
81         return error;
82 }
83
84 /**
85  * device_resume - resume all the devices in the system
86  * @level:      stage of resume process we're at 
87  * 
88  * Similar to device_suspend above, though we want to do a breadth-first
89  * walk of the tree to make sure we wake up parents before children.
90  * So, we iterate over the list backward. 
91  */
92 void device_resume(u32 level)
93 {
94         struct device * dev;
95
96         switch (level) {
97         case RESUME_POWER_ON:
98                 sysdev_resume();
99                 break;
100         case RESUME_RESTORE_STATE:
101                 sysdev_restore();
102                 break;
103         default:
104                 break;
105         }
106
107         down_write(&devices_subsys.rwsem);
108         list_for_each_entry(dev,&devices_subsys.kset.list,kobj.entry) {
109                 if (dev->driver && dev->driver->resume) {
110                         pr_debug("resuming device %s\n",dev->name);
111                         dev->driver->resume(dev,level);
112                 }
113         }
114         up_write(&devices_subsys.rwsem);
115 }
116
117 /**
118  * device_shutdown - call ->remove() on each device to shutdown. 
119  */
120 void device_shutdown(void)
121 {
122         struct device * dev;
123         
124         down_write(&devices_subsys.rwsem);
125         list_for_each_entry_reverse(dev,&devices_subsys.kset.list,kobj.entry) {
126                 pr_debug("shutting down %s: ",dev->name);
127                 if (dev->driver && dev->driver->shutdown) {
128                         pr_debug("Ok\n");
129                         dev->driver->shutdown(dev);
130                 } else
131                         pr_debug("Ignored.\n");
132         }
133         up_write(&devices_subsys.rwsem);
134
135         sysdev_shutdown();
136 }
137
138 EXPORT_SYMBOL(device_suspend);
139 EXPORT_SYMBOL(device_resume);
140 EXPORT_SYMBOL(device_shutdown);