summaryrefslogtreecommitdiffstats
path: root/drivers/base/power/resume.c
blob: 7cb62d62c958957bd6c88b2c0783ef9681712670 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
/*
 * resume.c - Functions for waking devices up.
 *
 * Copyright (c) 2003 Patrick Mochel
 * Copyright (c) 2003 Open Source Development Labs
 *
 * This file is released under the GPLv2
 *
 */

#include <linux/device.h>
#include <linux/resume-trace.h>
#include "../base.h"
#include "power.h"


/**
 *	resume_device - Restore state for one device.
 *	@dev:	Device.
 *
 */

int resume_device(struct device * dev)
{
	int error = 0;

	TRACE_DEVICE(dev);
	TRACE_RESUME(0);
	down(&dev->sem);
	if (dev->power.pm_parent
			&& dev->power.pm_parent->power.power_state.event) {
		dev_err(dev, "PM: resume from %d, parent %s still %d\n",
			dev->power.power_state.event,
			dev->power.pm_parent->bus_id,
			dev->power.pm_parent->power.power_state.event);
	}
	if (dev->bus && dev->bus->resume) {
		dev_dbg(dev,"resuming\n");
		error = dev->bus->resume(dev);
	}
	if (dev->class && dev->class->resume) {
		dev_dbg(dev,"class resume\n");
		error = dev->class->resume(dev);
	}
	up(&dev->sem);
	TRACE_RESUME(error);
	return error;
}


static int resume_device_early(struct device * dev)
{
	int error = 0;

	TRACE_DEVICE(dev);
	TRACE_RESUME(0);
	if (dev->bus && dev->bus->resume_early) {
		dev_dbg(dev,"EARLY resume\n");
		error = dev->bus->resume_early(dev);
	}
	TRACE_RESUME(error);
	return error;
}

/*
 * Resume the devices that have either not gone through
 * the late suspend, or that did go through it but also
 * went through the early resume
 */
void dpm_resume(void)
{
	down(&dpm_list_sem);
	while(!list_empty(&dpm_off)) {
		struct list_head * entry = dpm_off.next;
		struct device * dev = to_device(entry);

		get_device(dev);
		list_move_tail(entry, &dpm_active);

		up(&dpm_list_sem);
		if (!dev->power.prev_state.event)
			resume_device(dev);
		down(&dpm_list_sem);
		put_device(dev);
	}
	up(&dpm_list_sem);
}


/**
 *	device_resume - Restore state of each device in system.
 *
 *	Walk the dpm_off list, remove each entry, resume the device,
 *	then add it to the dpm_active list.
 */

void device_resume(void)
{
	might_sleep();
	down(&dpm_sem);
	dpm_resume();
	up(&dpm_sem);
}

EXPORT_SYMBOL_GPL(device_resume);


/**
 *	device_power_up_irq - Power on some devices.
 *
 *	Walk the dpm_off_irq list and power each device up. This
 *	is used for devices that required they be powered down with
 *	interrupts disabled. As devices are powered on, they are moved to
 *	the dpm_suspended list.
 *
 *	Interrupts must be disabled when calling this.
 */

void dpm_power_up(void)
{
	while(!list_empty(&dpm_off_irq)) {
		struct list_head * entry = dpm_off_irq.next;
		struct device * dev = to_device(entry);

		list_move_tail(entry, &dpm_off);
		resume_device_early(dev);
	}
}


/**
 *	device_pm_power_up - Turn on all devices that need special attention.
 *
 *	Power on system devices then devices that required we shut them down
 *	with interrupts disabled.
 *	Called with interrupts disabled.
 */

void device_power_up(void)
{
	sysdev_resume();
	dpm_power_up();
}

EXPORT_SYMBOL_GPL(device_power_up);