xref: /linux-6.15/kernel/power/main.c (revision 1da177e4)
1*1da177e4SLinus Torvalds /*
2*1da177e4SLinus Torvalds  * kernel/power/main.c - PM subsystem core functionality.
3*1da177e4SLinus Torvalds  *
4*1da177e4SLinus Torvalds  * Copyright (c) 2003 Patrick Mochel
5*1da177e4SLinus Torvalds  * Copyright (c) 2003 Open Source Development Lab
6*1da177e4SLinus Torvalds  *
7*1da177e4SLinus Torvalds  * This file is released under the GPLv2
8*1da177e4SLinus Torvalds  *
9*1da177e4SLinus Torvalds  */
10*1da177e4SLinus Torvalds 
11*1da177e4SLinus Torvalds #include <linux/suspend.h>
12*1da177e4SLinus Torvalds #include <linux/kobject.h>
13*1da177e4SLinus Torvalds #include <linux/string.h>
14*1da177e4SLinus Torvalds #include <linux/delay.h>
15*1da177e4SLinus Torvalds #include <linux/errno.h>
16*1da177e4SLinus Torvalds #include <linux/init.h>
17*1da177e4SLinus Torvalds #include <linux/pm.h>
18*1da177e4SLinus Torvalds 
19*1da177e4SLinus Torvalds 
20*1da177e4SLinus Torvalds #include "power.h"
21*1da177e4SLinus Torvalds 
22*1da177e4SLinus Torvalds DECLARE_MUTEX(pm_sem);
23*1da177e4SLinus Torvalds 
24*1da177e4SLinus Torvalds struct pm_ops * pm_ops = NULL;
25*1da177e4SLinus Torvalds suspend_disk_method_t pm_disk_mode = PM_DISK_SHUTDOWN;
26*1da177e4SLinus Torvalds 
27*1da177e4SLinus Torvalds /**
28*1da177e4SLinus Torvalds  *	pm_set_ops - Set the global power method table.
29*1da177e4SLinus Torvalds  *	@ops:	Pointer to ops structure.
30*1da177e4SLinus Torvalds  */
31*1da177e4SLinus Torvalds 
32*1da177e4SLinus Torvalds void pm_set_ops(struct pm_ops * ops)
33*1da177e4SLinus Torvalds {
34*1da177e4SLinus Torvalds 	down(&pm_sem);
35*1da177e4SLinus Torvalds 	pm_ops = ops;
36*1da177e4SLinus Torvalds 	up(&pm_sem);
37*1da177e4SLinus Torvalds }
38*1da177e4SLinus Torvalds 
39*1da177e4SLinus Torvalds 
40*1da177e4SLinus Torvalds /**
41*1da177e4SLinus Torvalds  *	suspend_prepare - Do prep work before entering low-power state.
42*1da177e4SLinus Torvalds  *	@state:		State we're entering.
43*1da177e4SLinus Torvalds  *
44*1da177e4SLinus Torvalds  *	This is common code that is called for each state that we're
45*1da177e4SLinus Torvalds  *	entering. Allocate a console, stop all processes, then make sure
46*1da177e4SLinus Torvalds  *	the platform can enter the requested state.
47*1da177e4SLinus Torvalds  */
48*1da177e4SLinus Torvalds 
49*1da177e4SLinus Torvalds static int suspend_prepare(suspend_state_t state)
50*1da177e4SLinus Torvalds {
51*1da177e4SLinus Torvalds 	int error = 0;
52*1da177e4SLinus Torvalds 
53*1da177e4SLinus Torvalds 	if (!pm_ops || !pm_ops->enter)
54*1da177e4SLinus Torvalds 		return -EPERM;
55*1da177e4SLinus Torvalds 
56*1da177e4SLinus Torvalds 	pm_prepare_console();
57*1da177e4SLinus Torvalds 
58*1da177e4SLinus Torvalds 	if (freeze_processes()) {
59*1da177e4SLinus Torvalds 		error = -EAGAIN;
60*1da177e4SLinus Torvalds 		goto Thaw;
61*1da177e4SLinus Torvalds 	}
62*1da177e4SLinus Torvalds 
63*1da177e4SLinus Torvalds 	if (pm_ops->prepare) {
64*1da177e4SLinus Torvalds 		if ((error = pm_ops->prepare(state)))
65*1da177e4SLinus Torvalds 			goto Thaw;
66*1da177e4SLinus Torvalds 	}
67*1da177e4SLinus Torvalds 
68*1da177e4SLinus Torvalds 	if ((error = device_suspend(PMSG_SUSPEND))) {
69*1da177e4SLinus Torvalds 		printk(KERN_ERR "Some devices failed to suspend\n");
70*1da177e4SLinus Torvalds 		goto Finish;
71*1da177e4SLinus Torvalds 	}
72*1da177e4SLinus Torvalds 	return 0;
73*1da177e4SLinus Torvalds  Finish:
74*1da177e4SLinus Torvalds 	if (pm_ops->finish)
75*1da177e4SLinus Torvalds 		pm_ops->finish(state);
76*1da177e4SLinus Torvalds  Thaw:
77*1da177e4SLinus Torvalds 	thaw_processes();
78*1da177e4SLinus Torvalds 	pm_restore_console();
79*1da177e4SLinus Torvalds 	return error;
80*1da177e4SLinus Torvalds }
81*1da177e4SLinus Torvalds 
82*1da177e4SLinus Torvalds 
83*1da177e4SLinus Torvalds static int suspend_enter(suspend_state_t state)
84*1da177e4SLinus Torvalds {
85*1da177e4SLinus Torvalds 	int error = 0;
86*1da177e4SLinus Torvalds 	unsigned long flags;
87*1da177e4SLinus Torvalds 
88*1da177e4SLinus Torvalds 	local_irq_save(flags);
89*1da177e4SLinus Torvalds 
90*1da177e4SLinus Torvalds 	if ((error = device_power_down(PMSG_SUSPEND))) {
91*1da177e4SLinus Torvalds 		printk(KERN_ERR "Some devices failed to power down\n");
92*1da177e4SLinus Torvalds 		goto Done;
93*1da177e4SLinus Torvalds 	}
94*1da177e4SLinus Torvalds 	error = pm_ops->enter(state);
95*1da177e4SLinus Torvalds 	device_power_up();
96*1da177e4SLinus Torvalds  Done:
97*1da177e4SLinus Torvalds 	local_irq_restore(flags);
98*1da177e4SLinus Torvalds 	return error;
99*1da177e4SLinus Torvalds }
100*1da177e4SLinus Torvalds 
101*1da177e4SLinus Torvalds 
102*1da177e4SLinus Torvalds /**
103*1da177e4SLinus Torvalds  *	suspend_finish - Do final work before exiting suspend sequence.
104*1da177e4SLinus Torvalds  *	@state:		State we're coming out of.
105*1da177e4SLinus Torvalds  *
106*1da177e4SLinus Torvalds  *	Call platform code to clean up, restart processes, and free the
107*1da177e4SLinus Torvalds  *	console that we've allocated. This is not called for suspend-to-disk.
108*1da177e4SLinus Torvalds  */
109*1da177e4SLinus Torvalds 
110*1da177e4SLinus Torvalds static void suspend_finish(suspend_state_t state)
111*1da177e4SLinus Torvalds {
112*1da177e4SLinus Torvalds 	device_resume();
113*1da177e4SLinus Torvalds 	if (pm_ops && pm_ops->finish)
114*1da177e4SLinus Torvalds 		pm_ops->finish(state);
115*1da177e4SLinus Torvalds 	thaw_processes();
116*1da177e4SLinus Torvalds 	pm_restore_console();
117*1da177e4SLinus Torvalds }
118*1da177e4SLinus Torvalds 
119*1da177e4SLinus Torvalds 
120*1da177e4SLinus Torvalds 
121*1da177e4SLinus Torvalds 
122*1da177e4SLinus Torvalds static char * pm_states[] = {
123*1da177e4SLinus Torvalds 	[PM_SUSPEND_STANDBY]	= "standby",
124*1da177e4SLinus Torvalds 	[PM_SUSPEND_MEM]	= "mem",
125*1da177e4SLinus Torvalds 	[PM_SUSPEND_DISK]	= "disk",
126*1da177e4SLinus Torvalds 	NULL,
127*1da177e4SLinus Torvalds };
128*1da177e4SLinus Torvalds 
129*1da177e4SLinus Torvalds 
130*1da177e4SLinus Torvalds /**
131*1da177e4SLinus Torvalds  *	enter_state - Do common work of entering low-power state.
132*1da177e4SLinus Torvalds  *	@state:		pm_state structure for state we're entering.
133*1da177e4SLinus Torvalds  *
134*1da177e4SLinus Torvalds  *	Make sure we're the only ones trying to enter a sleep state. Fail
135*1da177e4SLinus Torvalds  *	if someone has beat us to it, since we don't want anything weird to
136*1da177e4SLinus Torvalds  *	happen when we wake up.
137*1da177e4SLinus Torvalds  *	Then, do the setup for suspend, enter the state, and cleaup (after
138*1da177e4SLinus Torvalds  *	we've woken up).
139*1da177e4SLinus Torvalds  */
140*1da177e4SLinus Torvalds 
141*1da177e4SLinus Torvalds static int enter_state(suspend_state_t state)
142*1da177e4SLinus Torvalds {
143*1da177e4SLinus Torvalds 	int error;
144*1da177e4SLinus Torvalds 
145*1da177e4SLinus Torvalds 	if (down_trylock(&pm_sem))
146*1da177e4SLinus Torvalds 		return -EBUSY;
147*1da177e4SLinus Torvalds 
148*1da177e4SLinus Torvalds 	if (state == PM_SUSPEND_DISK) {
149*1da177e4SLinus Torvalds 		error = pm_suspend_disk();
150*1da177e4SLinus Torvalds 		goto Unlock;
151*1da177e4SLinus Torvalds 	}
152*1da177e4SLinus Torvalds 
153*1da177e4SLinus Torvalds 	/* Suspend is hard to get right on SMP. */
154*1da177e4SLinus Torvalds 	if (num_online_cpus() != 1) {
155*1da177e4SLinus Torvalds 		error = -EPERM;
156*1da177e4SLinus Torvalds 		goto Unlock;
157*1da177e4SLinus Torvalds 	}
158*1da177e4SLinus Torvalds 
159*1da177e4SLinus Torvalds 	pr_debug("PM: Preparing system for suspend\n");
160*1da177e4SLinus Torvalds 	if ((error = suspend_prepare(state)))
161*1da177e4SLinus Torvalds 		goto Unlock;
162*1da177e4SLinus Torvalds 
163*1da177e4SLinus Torvalds 	pr_debug("PM: Entering state.\n");
164*1da177e4SLinus Torvalds 	error = suspend_enter(state);
165*1da177e4SLinus Torvalds 
166*1da177e4SLinus Torvalds 	pr_debug("PM: Finishing up.\n");
167*1da177e4SLinus Torvalds 	suspend_finish(state);
168*1da177e4SLinus Torvalds  Unlock:
169*1da177e4SLinus Torvalds 	up(&pm_sem);
170*1da177e4SLinus Torvalds 	return error;
171*1da177e4SLinus Torvalds }
172*1da177e4SLinus Torvalds 
173*1da177e4SLinus Torvalds /*
174*1da177e4SLinus Torvalds  * This is main interface to the outside world. It needs to be
175*1da177e4SLinus Torvalds  * called from process context.
176*1da177e4SLinus Torvalds  */
177*1da177e4SLinus Torvalds int software_suspend(void)
178*1da177e4SLinus Torvalds {
179*1da177e4SLinus Torvalds 	return enter_state(PM_SUSPEND_DISK);
180*1da177e4SLinus Torvalds }
181*1da177e4SLinus Torvalds 
182*1da177e4SLinus Torvalds 
183*1da177e4SLinus Torvalds /**
184*1da177e4SLinus Torvalds  *	pm_suspend - Externally visible function for suspending system.
185*1da177e4SLinus Torvalds  *	@state:		Enumarted value of state to enter.
186*1da177e4SLinus Torvalds  *
187*1da177e4SLinus Torvalds  *	Determine whether or not value is within range, get state
188*1da177e4SLinus Torvalds  *	structure, and enter (above).
189*1da177e4SLinus Torvalds  */
190*1da177e4SLinus Torvalds 
191*1da177e4SLinus Torvalds int pm_suspend(suspend_state_t state)
192*1da177e4SLinus Torvalds {
193*1da177e4SLinus Torvalds 	if (state > PM_SUSPEND_ON && state < PM_SUSPEND_MAX)
194*1da177e4SLinus Torvalds 		return enter_state(state);
195*1da177e4SLinus Torvalds 	return -EINVAL;
196*1da177e4SLinus Torvalds }
197*1da177e4SLinus Torvalds 
198*1da177e4SLinus Torvalds 
199*1da177e4SLinus Torvalds 
200*1da177e4SLinus Torvalds decl_subsys(power,NULL,NULL);
201*1da177e4SLinus Torvalds 
202*1da177e4SLinus Torvalds 
203*1da177e4SLinus Torvalds /**
204*1da177e4SLinus Torvalds  *	state - control system power state.
205*1da177e4SLinus Torvalds  *
206*1da177e4SLinus Torvalds  *	show() returns what states are supported, which is hard-coded to
207*1da177e4SLinus Torvalds  *	'standby' (Power-On Suspend), 'mem' (Suspend-to-RAM), and
208*1da177e4SLinus Torvalds  *	'disk' (Suspend-to-Disk).
209*1da177e4SLinus Torvalds  *
210*1da177e4SLinus Torvalds  *	store() accepts one of those strings, translates it into the
211*1da177e4SLinus Torvalds  *	proper enumerated value, and initiates a suspend transition.
212*1da177e4SLinus Torvalds  */
213*1da177e4SLinus Torvalds 
214*1da177e4SLinus Torvalds static ssize_t state_show(struct subsystem * subsys, char * buf)
215*1da177e4SLinus Torvalds {
216*1da177e4SLinus Torvalds 	int i;
217*1da177e4SLinus Torvalds 	char * s = buf;
218*1da177e4SLinus Torvalds 
219*1da177e4SLinus Torvalds 	for (i = 0; i < PM_SUSPEND_MAX; i++) {
220*1da177e4SLinus Torvalds 		if (pm_states[i])
221*1da177e4SLinus Torvalds 			s += sprintf(s,"%s ",pm_states[i]);
222*1da177e4SLinus Torvalds 	}
223*1da177e4SLinus Torvalds 	s += sprintf(s,"\n");
224*1da177e4SLinus Torvalds 	return (s - buf);
225*1da177e4SLinus Torvalds }
226*1da177e4SLinus Torvalds 
227*1da177e4SLinus Torvalds static ssize_t state_store(struct subsystem * subsys, const char * buf, size_t n)
228*1da177e4SLinus Torvalds {
229*1da177e4SLinus Torvalds 	suspend_state_t state = PM_SUSPEND_STANDBY;
230*1da177e4SLinus Torvalds 	char ** s;
231*1da177e4SLinus Torvalds 	char *p;
232*1da177e4SLinus Torvalds 	int error;
233*1da177e4SLinus Torvalds 	int len;
234*1da177e4SLinus Torvalds 
235*1da177e4SLinus Torvalds 	p = memchr(buf, '\n', n);
236*1da177e4SLinus Torvalds 	len = p ? p - buf : n;
237*1da177e4SLinus Torvalds 
238*1da177e4SLinus Torvalds 	for (s = &pm_states[state]; state < PM_SUSPEND_MAX; s++, state++) {
239*1da177e4SLinus Torvalds 		if (*s && !strncmp(buf, *s, len))
240*1da177e4SLinus Torvalds 			break;
241*1da177e4SLinus Torvalds 	}
242*1da177e4SLinus Torvalds 	if (*s)
243*1da177e4SLinus Torvalds 		error = enter_state(state);
244*1da177e4SLinus Torvalds 	else
245*1da177e4SLinus Torvalds 		error = -EINVAL;
246*1da177e4SLinus Torvalds 	return error ? error : n;
247*1da177e4SLinus Torvalds }
248*1da177e4SLinus Torvalds 
249*1da177e4SLinus Torvalds power_attr(state);
250*1da177e4SLinus Torvalds 
251*1da177e4SLinus Torvalds static struct attribute * g[] = {
252*1da177e4SLinus Torvalds 	&state_attr.attr,
253*1da177e4SLinus Torvalds 	NULL,
254*1da177e4SLinus Torvalds };
255*1da177e4SLinus Torvalds 
256*1da177e4SLinus Torvalds static struct attribute_group attr_group = {
257*1da177e4SLinus Torvalds 	.attrs = g,
258*1da177e4SLinus Torvalds };
259*1da177e4SLinus Torvalds 
260*1da177e4SLinus Torvalds 
261*1da177e4SLinus Torvalds static int __init pm_init(void)
262*1da177e4SLinus Torvalds {
263*1da177e4SLinus Torvalds 	int error = subsystem_register(&power_subsys);
264*1da177e4SLinus Torvalds 	if (!error)
265*1da177e4SLinus Torvalds 		error = sysfs_create_group(&power_subsys.kset.kobj,&attr_group);
266*1da177e4SLinus Torvalds 	return error;
267*1da177e4SLinus Torvalds }
268*1da177e4SLinus Torvalds 
269*1da177e4SLinus Torvalds core_initcall(pm_init);
270