blob: 0c961068854268d384c2da354c7e12daafaaaff9 [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);
Rafael J. Wysocki515c5352007-06-17 19:48:06 +020091 resume_device(dev);
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070092 mutex_lock(&dpm_list_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 put_device(dev);
94 }
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -070095 mutex_unlock(&dpm_list_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -070096}
97
98
99/**
100 * device_resume - Restore state of each device in system.
101 *
102 * Walk the dpm_off list, remove each entry, resume the device,
103 * then add it to the dpm_active list.
104 */
105
106void device_resume(void)
107{
Pavel Machekbb84c892006-08-31 22:02:11 -0700108 might_sleep();
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -0700109 mutex_lock(&dpm_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700110 dpm_resume();
Matthias Kaehlcke11048dc2007-05-23 14:19:41 -0700111 mutex_unlock(&dpm_mtx);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700112}
113
114EXPORT_SYMBOL_GPL(device_resume);
115
116
117/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400118 * dpm_power_up - Power on some devices.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700119 *
120 * Walk the dpm_off_irq list and power each device up. This
121 * is used for devices that required they be powered down with
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400122 * interrupts disabled. As devices are powered on, they are moved
123 * to the dpm_active list.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700124 *
125 * Interrupts must be disabled when calling this.
126 */
127
128void dpm_power_up(void)
129{
130 while(!list_empty(&dpm_off_irq)) {
131 struct list_head * entry = dpm_off_irq.next;
132 struct device * dev = to_device(entry);
133
Linus Torvalds7c8265f2006-06-24 14:50:29 -0700134 list_move_tail(entry, &dpm_off);
135 resume_device_early(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700136 }
137}
138
139
140/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400141 * device_power_up - Turn on all devices that need special attention.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700142 *
143 * Power on system devices then devices that required we shut them down
144 * with interrupts disabled.
145 * Called with interrupts disabled.
146 */
147
148void device_power_up(void)
149{
150 sysdev_resume();
151 dpm_power_up();
152}
153
154EXPORT_SYMBOL_GPL(device_power_up);
155
156