diff options
Diffstat (limited to 'drivers/base/power/suspend.c')
-rw-r--r-- | drivers/base/power/suspend.c | 92 |
1 files changed, 62 insertions, 30 deletions
diff --git a/drivers/base/power/suspend.c b/drivers/base/power/suspend.c index 69509e02f703..ece136bf97e3 100644 --- a/drivers/base/power/suspend.c +++ b/drivers/base/power/suspend.c | |||
@@ -34,6 +34,7 @@ static inline char *suspend_verb(u32 event) | |||
34 | switch (event) { | 34 | switch (event) { |
35 | case PM_EVENT_SUSPEND: return "suspend"; | 35 | case PM_EVENT_SUSPEND: return "suspend"; |
36 | case PM_EVENT_FREEZE: return "freeze"; | 36 | case PM_EVENT_FREEZE: return "freeze"; |
37 | case PM_EVENT_PRETHAW: return "prethaw"; | ||
37 | default: return "(unknown suspend event)"; | 38 | default: return "(unknown suspend event)"; |
38 | } | 39 | } |
39 | } | 40 | } |
@@ -65,7 +66,19 @@ int suspend_device(struct device * dev, pm_message_t state) | |||
65 | 66 | ||
66 | dev->power.prev_state = dev->power.power_state; | 67 | dev->power.prev_state = dev->power.power_state; |
67 | 68 | ||
68 | if (dev->bus && dev->bus->suspend && !dev->power.power_state.event) { | 69 | if (dev->class && dev->class->suspend && !dev->power.power_state.event) { |
70 | dev_dbg(dev, "class %s%s\n", | ||
71 | suspend_verb(state.event), | ||
72 | ((state.event == PM_EVENT_SUSPEND) | ||
73 | && device_may_wakeup(dev)) | ||
74 | ? ", may wakeup" | ||
75 | : "" | ||
76 | ); | ||
77 | error = dev->class->suspend(dev, state); | ||
78 | suspend_report_result(dev->class->suspend, error); | ||
79 | } | ||
80 | |||
81 | if (!error && dev->bus && dev->bus->suspend && !dev->power.power_state.event) { | ||
69 | dev_dbg(dev, "%s%s\n", | 82 | dev_dbg(dev, "%s%s\n", |
70 | suspend_verb(state.event), | 83 | suspend_verb(state.event), |
71 | ((state.event == PM_EVENT_SUSPEND) | 84 | ((state.event == PM_EVENT_SUSPEND) |
@@ -81,15 +94,42 @@ int suspend_device(struct device * dev, pm_message_t state) | |||
81 | } | 94 | } |
82 | 95 | ||
83 | 96 | ||
97 | /* | ||
98 | * This is called with interrupts off, only a single CPU | ||
99 | * running. We can't do down() on a semaphore (and we don't | ||
100 | * need the protection) | ||
101 | */ | ||
102 | static int suspend_device_late(struct device *dev, pm_message_t state) | ||
103 | { | ||
104 | int error = 0; | ||
105 | |||
106 | if (dev->bus && dev->bus->suspend_late && !dev->power.power_state.event) { | ||
107 | dev_dbg(dev, "LATE %s%s\n", | ||
108 | suspend_verb(state.event), | ||
109 | ((state.event == PM_EVENT_SUSPEND) | ||
110 | && device_may_wakeup(dev)) | ||
111 | ? ", may wakeup" | ||
112 | : "" | ||
113 | ); | ||
114 | error = dev->bus->suspend_late(dev, state); | ||
115 | suspend_report_result(dev->bus->suspend_late, error); | ||
116 | } | ||
117 | return error; | ||
118 | } | ||
119 | |||
84 | /** | 120 | /** |
85 | * device_suspend - Save state and stop all devices in system. | 121 | * device_suspend - Save state and stop all devices in system. |
86 | * @state: Power state to put each device in. | 122 | * @state: Power state to put each device in. |
87 | * | 123 | * |
88 | * Walk the dpm_active list, call ->suspend() for each device, and move | 124 | * Walk the dpm_active list, call ->suspend() for each device, and move |
89 | * it to dpm_off. | 125 | * it to the dpm_off list. |
90 | * Check the return value for each. If it returns 0, then we move the | 126 | * |
91 | * the device to the dpm_off list. If it returns -EAGAIN, we move it to | 127 | * (For historical reasons, if it returns -EAGAIN, that used to mean |
92 | * the dpm_off_irq list. If we get a different error, try and back out. | 128 | * that the device would be called again with interrupts disabled. |
129 | * These days, we use the "suspend_late()" callback for that, so we | ||
130 | * print a warning and consider it an error). | ||
131 | * | ||
132 | * If we get a different error, try and back out. | ||
93 | * | 133 | * |
94 | * If we hit a failure with any of the devices, call device_resume() | 134 | * If we hit a failure with any of the devices, call device_resume() |
95 | * above to bring the suspended devices back to life. | 135 | * above to bring the suspended devices back to life. |
@@ -100,6 +140,7 @@ int device_suspend(pm_message_t state) | |||
100 | { | 140 | { |
101 | int error = 0; | 141 | int error = 0; |
102 | 142 | ||
143 | might_sleep(); | ||
103 | down(&dpm_sem); | 144 | down(&dpm_sem); |
104 | down(&dpm_list_sem); | 145 | down(&dpm_list_sem); |
105 | while (!list_empty(&dpm_active) && error == 0) { | 146 | while (!list_empty(&dpm_active) && error == 0) { |
@@ -115,39 +156,27 @@ int device_suspend(pm_message_t state) | |||
115 | 156 | ||
116 | /* Check if the device got removed */ | 157 | /* Check if the device got removed */ |
117 | if (!list_empty(&dev->power.entry)) { | 158 | if (!list_empty(&dev->power.entry)) { |
118 | /* Move it to the dpm_off or dpm_off_irq list */ | 159 | /* Move it to the dpm_off list */ |
119 | if (!error) | 160 | if (!error) |
120 | list_move(&dev->power.entry, &dpm_off); | 161 | list_move(&dev->power.entry, &dpm_off); |
121 | else if (error == -EAGAIN) { | ||
122 | list_move(&dev->power.entry, &dpm_off_irq); | ||
123 | error = 0; | ||
124 | } | ||
125 | } | 162 | } |
126 | if (error) | 163 | if (error) |
127 | printk(KERN_ERR "Could not suspend device %s: " | 164 | printk(KERN_ERR "Could not suspend device %s: " |
128 | "error %d\n", kobject_name(&dev->kobj), error); | 165 | "error %d%s\n", |
166 | kobject_name(&dev->kobj), error, | ||
167 | error == -EAGAIN ? " (please convert to suspend_late)" : ""); | ||
129 | put_device(dev); | 168 | put_device(dev); |
130 | } | 169 | } |
131 | up(&dpm_list_sem); | 170 | up(&dpm_list_sem); |
132 | if (error) { | 171 | if (error) |
133 | /* we failed... before resuming, bring back devices from | ||
134 | * dpm_off_irq list back to main dpm_off list, we do want | ||
135 | * to call resume() on them, in case they partially suspended | ||
136 | * despite returning -EAGAIN | ||
137 | */ | ||
138 | while (!list_empty(&dpm_off_irq)) { | ||
139 | struct list_head * entry = dpm_off_irq.next; | ||
140 | list_move(entry, &dpm_off); | ||
141 | } | ||
142 | dpm_resume(); | 172 | dpm_resume(); |
143 | } | 173 | |
144 | up(&dpm_sem); | 174 | up(&dpm_sem); |
145 | return error; | 175 | return error; |
146 | } | 176 | } |
147 | 177 | ||
148 | EXPORT_SYMBOL_GPL(device_suspend); | 178 | EXPORT_SYMBOL_GPL(device_suspend); |
149 | 179 | ||
150 | |||
151 | /** | 180 | /** |
152 | * device_power_down - Shut down special devices. | 181 | * device_power_down - Shut down special devices. |
153 | * @state: Power state to enter. | 182 | * @state: Power state to enter. |
@@ -162,14 +191,17 @@ int device_power_down(pm_message_t state) | |||
162 | int error = 0; | 191 | int error = 0; |
163 | struct device * dev; | 192 | struct device * dev; |
164 | 193 | ||
165 | list_for_each_entry_reverse(dev, &dpm_off_irq, power.entry) { | 194 | while (!list_empty(&dpm_off)) { |
166 | if ((error = suspend_device(dev, state))) | 195 | struct list_head * entry = dpm_off.prev; |
167 | break; | 196 | |
197 | dev = to_device(entry); | ||
198 | error = suspend_device_late(dev, state); | ||
199 | if (error) | ||
200 | goto Error; | ||
201 | list_move(&dev->power.entry, &dpm_off_irq); | ||
168 | } | 202 | } |
169 | if (error) | 203 | |
170 | goto Error; | 204 | error = sysdev_suspend(state); |
171 | if ((error = sysdev_suspend(state))) | ||
172 | goto Error; | ||
173 | Done: | 205 | Done: |
174 | return error; | 206 | return error; |
175 | Error: | 207 | Error: |