blob: 99679e7a6cc625a8ff8158b47972e7b16080ee30 [file] [log] [blame]
Linus Torvalds1da177e2005-04-16 15:20:36 -07001/*
2 * resume.c - Functions for waking devices up.
3 *
4 * Copyright (c) 2003 Patrick Mochel
5 * Copyright (c) 2003 Open Source Development Labs
6 *
7 * This file is released under the GPLv2
8 *
9 */
10
11#include <linux/device.h>
Linus Torvaldsd02f40e2006-06-24 14:32:18 -070012#include <linux/resume-trace.h>
Adrian Bunkf67d1152006-01-19 17:30:17 +010013#include "../base.h"
Linus Torvalds1da177e2005-04-16 15:20:36 -070014#include "power.h"
15
Linus Torvalds1da177e2005-04-16 15:20:36 -070016
17/**
18 * resume_device - Restore state for one device.
19 * @dev: Device.
20 *
21 */
22
23int resume_device(struct device * dev)
24{
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080025 int error = 0;
26
Linus Torvaldsd02f40e2006-06-24 14:32:18 -070027 TRACE_DEVICE(dev);
28 TRACE_RESUME(0);
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040029
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080030 down(&dev->sem);
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040031
Rafael J. Wysocki9cddad72007-06-13 15:53:34 +020032 if (dev->parent && dev->parent->power.power_state.event) {
David Brownell82428b62005-05-09 08:07:00 -070033 dev_err(dev, "PM: resume from %d, parent %s still %d\n",
Pavel Machekca078ba2005-09-03 15:56:57 -070034 dev->power.power_state.event,
Rafael J. Wysocki9cddad72007-06-13 15:53:34 +020035 dev->parent->bus_id,
36 dev->parent->power.power_state.event);
David Brownell82428b62005-05-09 08:07:00 -070037 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040038
David Brownell82428b62005-05-09 08:07:00 -070039 if (dev->bus && dev->bus->resume) {
40 dev_dbg(dev,"resuming\n");
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080041 error = dev->bus->resume(dev);
David Brownell82428b62005-05-09 08:07:00 -070042 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040043
44 if (!error && dev->type && dev->type->resume) {
45 dev_dbg(dev,"resuming\n");
46 error = dev->type->resume(dev);
47 }
48
49 if (!error && dev->class && dev->class->resume) {
Linus Torvalds7c8265f2006-06-24 14:50:29 -070050 dev_dbg(dev,"class resume\n");
51 error = dev->class->resume(dev);
52 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040053
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080054 up(&dev->sem);
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040055
Linus Torvaldsd02f40e2006-06-24 14:32:18 -070056 TRACE_RESUME(error);
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080057 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -070058}
59
60
Linus Torvalds7c8265f2006-06-24 14:50:29 -070061static int resume_device_early(struct device * dev)
62{
63 int error = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070064
Linus Torvalds7c8265f2006-06-24 14:50:29 -070065 TRACE_DEVICE(dev);
66 TRACE_RESUME(0);
67 if (dev->bus && dev->bus->resume_early) {
68 dev_dbg(dev,"EARLY resume\n");
69 error = dev->bus->resume_early(dev);
70 }
71 TRACE_RESUME(error);
72 return error;
73}
74
75/*
76 * Resume the devices that have either not gone through
77 * the late suspend, or that did go through it but also
78 * went through the early resume
79 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070080void dpm_resume(void)
81{
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070082 mutex_lock(&dpm_list_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070083 while(!list_empty(&dpm_off)) {
84 struct list_head * entry = dpm_off.next;
85 struct device * dev = to_device(entry);
86
87 get_device(dev);
Akinobu Mita1bfba4e2006-06-26 00:24:40 -070088 list_move_tail(entry, &dpm_active);
Linus Torvalds1da177e2005-04-16 15:20:36 -070089
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070090 mutex_unlock(&dpm_list_mtx);
Pavel Machekca078ba2005-09-03 15:56:57 -070091 if (!dev->power.prev_state.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -070092 resume_device(dev);
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070093 mutex_lock(&dpm_list_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070094 put_device(dev);
95 }
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070096 mutex_unlock(&dpm_list_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070097}
98
99
100/**
101 * device_resume - Restore state of each device in system.
102 *
103 * Walk the dpm_off list, remove each entry, resume the device,
104 * then add it to the dpm_active list.
105 */
106
107void device_resume(void)
108{
Pavel Machekbb84c892006-08-31 22:02:11 -0700109 might_sleep();
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -0700110 mutex_lock(&dpm_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 dpm_resume();
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -0700112 mutex_unlock(&dpm_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700113}
114
115EXPORT_SYMBOL_GPL(device_resume);
116
117
118/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400119 * dpm_power_up - Power on some devices.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700120 *
121 * Walk the dpm_off_irq list and power each device up. This
122 * is used for devices that required they be powered down with
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400123 * interrupts disabled. As devices are powered on, they are moved
124 * to the dpm_active list.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700125 *
126 * Interrupts must be disabled when calling this.
127 */
128
129void dpm_power_up(void)
130{
131 while(!list_empty(&dpm_off_irq)) {
132 struct list_head * entry = dpm_off_irq.next;
133 struct device * dev = to_device(entry);
134
Linus Torvalds7c8265f2006-06-24 14:50:29 -0700135 list_move_tail(entry, &dpm_off);
136 resume_device_early(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700137 }
138}
139
140
141/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400142 * device_power_up - Turn on all devices that need special attention.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700143 *
144 * Power on system devices then devices that required we shut them down
145 * with interrupts disabled.
146 * Called with interrupts disabled.
147 */
148
149void device_power_up(void)
150{
151 sysdev_resume();
152 dpm_power_up();
153}
154
155EXPORT_SYMBOL_GPL(device_power_up);
156
157