xref: /openbmc/linux/kernel/power/console.c (revision 498495dba268b20e8eadd7fe93c140c68b6cc9d2)
1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
21da177e4SLinus Torvalds /*
337cce26bSH Hartley Sweeten  * Functions for saving/restoring console.
41da177e4SLinus Torvalds  *
51da177e4SLinus Torvalds  * Originally from swsusp.
61da177e4SLinus Torvalds  */
71da177e4SLinus Torvalds 
8f43f627dSJesse Barnes #include <linux/console.h>
91da177e4SLinus Torvalds #include <linux/vt_kern.h>
101da177e4SLinus Torvalds #include <linux/kbd_kern.h>
115ada918bSBernhard Walle #include <linux/vt.h>
12b6f448e9SAndres Salomon #include <linux/module.h>
131ff6bbfdSTejun Heo #include <linux/slab.h>
141da177e4SLinus Torvalds #include "power.h"
151da177e4SLinus Torvalds 
1646cd2f32SRafael J. Wysocki #define SUSPEND_CONSOLE	(MAX_NR_CONSOLES-1)
1746cd2f32SRafael J. Wysocki 
181da177e4SLinus Torvalds static int orig_fgconsole, orig_kmsg;
191da177e4SLinus Torvalds 
20f43f627dSJesse Barnes static DEFINE_MUTEX(vt_switch_mutex);
21f43f627dSJesse Barnes 
22f43f627dSJesse Barnes struct pm_vt_switch {
23f43f627dSJesse Barnes 	struct list_head head;
24f43f627dSJesse Barnes 	struct device *dev;
25f43f627dSJesse Barnes 	bool required;
26f43f627dSJesse Barnes };
27f43f627dSJesse Barnes 
28f43f627dSJesse Barnes static LIST_HEAD(pm_vt_switch_list);
29f43f627dSJesse Barnes 
30f43f627dSJesse Barnes 
31f43f627dSJesse Barnes /**
32f43f627dSJesse Barnes  * pm_vt_switch_required - indicate VT switch at suspend requirements
33f43f627dSJesse Barnes  * @dev: device
34f43f627dSJesse Barnes  * @required: if true, caller needs VT switch at suspend/resume time
35f43f627dSJesse Barnes  *
36f43f627dSJesse Barnes  * The different console drivers may or may not require VT switches across
37f43f627dSJesse Barnes  * suspend/resume, depending on how they handle restoring video state and
38f43f627dSJesse Barnes  * what may be running.
39f43f627dSJesse Barnes  *
40f43f627dSJesse Barnes  * Drivers can indicate support for switchless suspend/resume, which can
41f43f627dSJesse Barnes  * save time and flicker, by using this routine and passing 'false' as
42f43f627dSJesse Barnes  * the argument.  If any loaded driver needs VT switching, or the
43f43f627dSJesse Barnes  * no_console_suspend argument has been passed on the command line, VT
44f43f627dSJesse Barnes  * switches will occur.
45f43f627dSJesse Barnes  */
pm_vt_switch_required(struct device * dev,bool required)46f43f627dSJesse Barnes void pm_vt_switch_required(struct device *dev, bool required)
47f43f627dSJesse Barnes {
48f43f627dSJesse Barnes 	struct pm_vt_switch *entry, *tmp;
49f43f627dSJesse Barnes 
50f43f627dSJesse Barnes 	mutex_lock(&vt_switch_mutex);
51f43f627dSJesse Barnes 	list_for_each_entry(tmp, &pm_vt_switch_list, head) {
52f43f627dSJesse Barnes 		if (tmp->dev == dev) {
53f43f627dSJesse Barnes 			/* already registered, update requirement */
54f43f627dSJesse Barnes 			tmp->required = required;
55f43f627dSJesse Barnes 			goto out;
56f43f627dSJesse Barnes 		}
57f43f627dSJesse Barnes 	}
58f43f627dSJesse Barnes 
59f43f627dSJesse Barnes 	entry = kmalloc(sizeof(*entry), GFP_KERNEL);
60f43f627dSJesse Barnes 	if (!entry)
61f43f627dSJesse Barnes 		goto out;
62f43f627dSJesse Barnes 
63f43f627dSJesse Barnes 	entry->required = required;
64f43f627dSJesse Barnes 	entry->dev = dev;
65f43f627dSJesse Barnes 
66f43f627dSJesse Barnes 	list_add(&entry->head, &pm_vt_switch_list);
67f43f627dSJesse Barnes out:
68f43f627dSJesse Barnes 	mutex_unlock(&vt_switch_mutex);
69f43f627dSJesse Barnes }
70f43f627dSJesse Barnes EXPORT_SYMBOL(pm_vt_switch_required);
71f43f627dSJesse Barnes 
72f43f627dSJesse Barnes /**
73f43f627dSJesse Barnes  * pm_vt_switch_unregister - stop tracking a device's VT switching needs
74f43f627dSJesse Barnes  * @dev: device
75f43f627dSJesse Barnes  *
76f43f627dSJesse Barnes  * Remove @dev from the vt switch list.
77f43f627dSJesse Barnes  */
pm_vt_switch_unregister(struct device * dev)78f43f627dSJesse Barnes void pm_vt_switch_unregister(struct device *dev)
79f43f627dSJesse Barnes {
80f43f627dSJesse Barnes 	struct pm_vt_switch *tmp;
81f43f627dSJesse Barnes 
82f43f627dSJesse Barnes 	mutex_lock(&vt_switch_mutex);
83f43f627dSJesse Barnes 	list_for_each_entry(tmp, &pm_vt_switch_list, head) {
84f43f627dSJesse Barnes 		if (tmp->dev == dev) {
85f43f627dSJesse Barnes 			list_del(&tmp->head);
86c6068504SMasami Ichikawa 			kfree(tmp);
87f43f627dSJesse Barnes 			break;
88f43f627dSJesse Barnes 		}
89f43f627dSJesse Barnes 	}
90f43f627dSJesse Barnes 	mutex_unlock(&vt_switch_mutex);
91f43f627dSJesse Barnes }
92f43f627dSJesse Barnes EXPORT_SYMBOL(pm_vt_switch_unregister);
93f43f627dSJesse Barnes 
94f43f627dSJesse Barnes /*
95f43f627dSJesse Barnes  * There are three cases when a VT switch on suspend/resume are required:
96f43f627dSJesse Barnes  *   1) no driver has indicated a requirement one way or another, so preserve
97f43f627dSJesse Barnes  *      the old behavior
98f43f627dSJesse Barnes  *   2) console suspend is disabled, we want to see debug messages across
99f43f627dSJesse Barnes  *      suspend/resume
100f43f627dSJesse Barnes  *   3) any registered driver indicates it needs a VT switch
101f43f627dSJesse Barnes  *
102f43f627dSJesse Barnes  * If none of these conditions is present, meaning we have at least one driver
103f43f627dSJesse Barnes  * that doesn't need the switch, and none that do, we can avoid it to make
104f43f627dSJesse Barnes  * resume look a little prettier (and suspend too, but that's usually hidden,
105f43f627dSJesse Barnes  * e.g. when closing the lid on a laptop).
106f43f627dSJesse Barnes  */
pm_vt_switch(void)107f43f627dSJesse Barnes static bool pm_vt_switch(void)
108f43f627dSJesse Barnes {
109f43f627dSJesse Barnes 	struct pm_vt_switch *entry;
110f43f627dSJesse Barnes 	bool ret = true;
111f43f627dSJesse Barnes 
112f43f627dSJesse Barnes 	mutex_lock(&vt_switch_mutex);
113f43f627dSJesse Barnes 	if (list_empty(&pm_vt_switch_list))
114f43f627dSJesse Barnes 		goto out;
115f43f627dSJesse Barnes 
116f43f627dSJesse Barnes 	if (!console_suspend_enabled)
117f43f627dSJesse Barnes 		goto out;
118f43f627dSJesse Barnes 
119f43f627dSJesse Barnes 	list_for_each_entry(entry, &pm_vt_switch_list, head) {
120f43f627dSJesse Barnes 		if (entry->required)
121f43f627dSJesse Barnes 			goto out;
122f43f627dSJesse Barnes 	}
123f43f627dSJesse Barnes 
124f43f627dSJesse Barnes 	ret = false;
125f43f627dSJesse Barnes out:
126f43f627dSJesse Barnes 	mutex_unlock(&vt_switch_mutex);
127f43f627dSJesse Barnes 	return ret;
128f43f627dSJesse Barnes }
129f43f627dSJesse Barnes 
pm_prepare_console(void)130ca5f2b4cSBorislav Petkov void pm_prepare_console(void)
1311da177e4SLinus Torvalds {
132f43f627dSJesse Barnes 	if (!pm_vt_switch())
133ca5f2b4cSBorislav Petkov 		return;
134f43f627dSJesse Barnes 
1358d233558SAlan Cox 	orig_fgconsole = vt_move_to_console(SUSPEND_CONSOLE, 1);
1368d233558SAlan Cox 	if (orig_fgconsole < 0)
137ca5f2b4cSBorislav Petkov 		return;
1381da177e4SLinus Torvalds 
1395ada918bSBernhard Walle 	orig_kmsg = vt_kmsg_redirect(SUSPEND_CONSOLE);
140ca5f2b4cSBorislav Petkov 	return;
1411da177e4SLinus Torvalds }
1421da177e4SLinus Torvalds 
pm_restore_console(void)1431da177e4SLinus Torvalds void pm_restore_console(void)
1441da177e4SLinus Torvalds {
145f43f627dSJesse Barnes 	if (!pm_vt_switch())
146f43f627dSJesse Barnes 		return;
147f43f627dSJesse Barnes 
1488d233558SAlan Cox 	if (orig_fgconsole >= 0) {
1498d233558SAlan Cox 		vt_move_to_console(orig_fgconsole, 0);
1505ada918bSBernhard Walle 		vt_kmsg_redirect(orig_kmsg);
1511da177e4SLinus Torvalds 	}
1528d233558SAlan Cox }
153