blob: a2c64188d7133fc95c31343592039f7ace18a64b [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
David Brownell82428b62005-05-09 08:07:00 -070032 if (dev->power.pm_parent
Pavel Machekca078ba2005-09-03 15:56:57 -070033 && dev->power.pm_parent->power.power_state.event) {
David Brownell82428b62005-05-09 08:07:00 -070034 dev_err(dev, "PM: resume from %d, parent %s still %d\n",
Pavel Machekca078ba2005-09-03 15:56:57 -070035 dev->power.power_state.event,
David Brownell82428b62005-05-09 08:07:00 -070036 dev->power.pm_parent->bus_id,
Pavel Machekca078ba2005-09-03 15:56:57 -070037 dev->power.pm_parent->power.power_state.event);
David Brownell82428b62005-05-09 08:07:00 -070038 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040039
David Brownell82428b62005-05-09 08:07:00 -070040 if (dev->bus && dev->bus->resume) {
41 dev_dbg(dev,"resuming\n");
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080042 error = dev->bus->resume(dev);
David Brownell82428b62005-05-09 08:07:00 -070043 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040044
45 if (!error && dev->type && dev->type->resume) {
46 dev_dbg(dev,"resuming\n");
47 error = dev->type->resume(dev);
48 }
49
50 if (!error && dev->class && dev->class->resume) {
Linus Torvalds7c8265f2006-06-24 14:50:29 -070051 dev_dbg(dev,"class resume\n");
52 error = dev->class->resume(dev);
53 }
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040054
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080055 up(&dev->sem);
Dmitry Torokhovf89cbc32007-04-03 01:08:40 -040056
Linus Torvaldsd02f40e2006-06-24 14:32:18 -070057 TRACE_RESUME(error);
mochel@digitalimplant.orgaf703162005-03-21 10:41:04 -080058 return error;
Linus Torvalds1da177e2005-04-16 15:20:36 -070059}
60
61
Linus Torvalds7c8265f2006-06-24 14:50:29 -070062static int resume_device_early(struct device * dev)
63{
64 int error = 0;
Linus Torvalds1da177e2005-04-16 15:20:36 -070065
Linus Torvalds7c8265f2006-06-24 14:50:29 -070066 TRACE_DEVICE(dev);
67 TRACE_RESUME(0);
68 if (dev->bus && dev->bus->resume_early) {
69 dev_dbg(dev,"EARLY resume\n");
70 error = dev->bus->resume_early(dev);
71 }
72 TRACE_RESUME(error);
73 return error;
74}
75
76/*
77 * Resume the devices that have either not gone through
78 * the late suspend, or that did go through it but also
79 * went through the early resume
80 */
Linus Torvalds1da177e2005-04-16 15:20:36 -070081void dpm_resume(void)
82{
83 down(&dpm_list_sem);
84 while(!list_empty(&dpm_off)) {
85 struct list_head * entry = dpm_off.next;
86 struct device * dev = to_device(entry);
87
88 get_device(dev);
Akinobu Mita1bfba4e2006-06-26 00:24:40 -070089 list_move_tail(entry, &dpm_active);
Linus Torvalds1da177e2005-04-16 15:20:36 -070090
91 up(&dpm_list_sem);
Pavel Machekca078ba2005-09-03 15:56:57 -070092 if (!dev->power.prev_state.event)
Linus Torvalds1da177e2005-04-16 15:20:36 -070093 resume_device(dev);
94 down(&dpm_list_sem);
95 put_device(dev);
96 }
97 up(&dpm_list_sem);
98}
99
100
101/**
102 * device_resume - Restore state of each device in system.
103 *
104 * Walk the dpm_off list, remove each entry, resume the device,
105 * then add it to the dpm_active list.
106 */
107
108void device_resume(void)
109{
Pavel Machekbb84c892006-08-31 22:02:11 -0700110 might_sleep();
Linus Torvalds1da177e2005-04-16 15:20:36 -0700111 down(&dpm_sem);
112 dpm_resume();
113 up(&dpm_sem);
114}
115
116EXPORT_SYMBOL_GPL(device_resume);
117
118
119/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400120 * dpm_power_up - Power on some devices.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700121 *
122 * Walk the dpm_off_irq list and power each device up. This
123 * is used for devices that required they be powered down with
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400124 * interrupts disabled. As devices are powered on, they are moved
125 * to the dpm_active list.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700126 *
127 * Interrupts must be disabled when calling this.
128 */
129
130void dpm_power_up(void)
131{
132 while(!list_empty(&dpm_off_irq)) {
133 struct list_head * entry = dpm_off_irq.next;
134 struct device * dev = to_device(entry);
135
Linus Torvalds7c8265f2006-06-24 14:50:29 -0700136 list_move_tail(entry, &dpm_off);
137 resume_device_early(dev);
Linus Torvalds1da177e2005-04-16 15:20:36 -0700138 }
139}
140
141
142/**
Dmitry Torokhov9de72ee2006-07-15 00:31:54 -0400143 * device_power_up - Turn on all devices that need special attention.
Linus Torvalds1da177e2005-04-16 15:20:36 -0700144 *
145 * Power on system devices then devices that required we shut them down
146 * with interrupts disabled.
147 * Called with interrupts disabled.
148 */
149
150void device_power_up(void)
151{
152 sysdev_resume();
153 dpm_power_up();
154}
155
156EXPORT_SYMBOL_GPL(device_power_up);
157
158