Power management sleep process

1. Power status described by Linux

  • On(on) S0 - Working

  • Standby (standby) S1 - CPU and RAM are powered but not executed

  • Suspend to RAM(mem) S3 - RAM is powered and the running content is saved to RAM

  • Suspend to Disk,Hibernation(disk) S4 - All content is saved to Disk and power down

S3 aka STR(suspend to ram), suspended to memory, referred to as standby. The computer stores the current operation status and other data in the memory, turns off the hard disk, peripherals and other equipment, and enters the waiting state. At this time, the memory still needs power to maintain its data, but the whole machine consumes little power. During recovery, the computer reads data from the memory and returns to the state before suspension. The recovery speed is fast. Optimizing the power consumption of DDR is the key to S3 performance. Most handheld devices use S3 for standby.

S4 aka STD(suspend to disk), suspend to hard disk, referred to as sleep. Store the operation status and other data in a file or a specific area on the hard disk, turn off the hard disk, peripherals and other devices, and enter the shutdown state. At this time, the computer is completely turned off without power consumption. During recovery, the computer reads data from the hibernation file / partition and returns to the state before hibernation. The recovery speed is slow.

II Add suspend command to u-boot
u-boot runs at full speed and consumes 83ma power
u-boot suspend: power consumption 33ma = = > external devices are not completely shut down, such as sound card and network card

How to enter sleep mode:
/* 1. Configure GPIO: for example, if you want to keep the LED on or off, the pin used to wake up the CPU should be set to interrupt function/
/ 2. Set INTMSK to mask all interrupts: in sleep mode, these pins are only used to wake up the system. When the CPU is running normally, you can reset INTMSK to use these pins for interrupt function/
/ 3. Configure wake source/
/ 4. Set MISCCR[13:12]=11b to make the USB module sleep/
/ 5. Save some values in gsstatus [4:3], which can be used when the system is awakened/
/ 6. Set the pull-up resistance of MISCCR[1:0] enable data bus/
/ 7. Clear lcdcon1 Environment to stop the LCD/
/ 8. Read these two registers: rREFRESH and rCLKCON to fill the TLB
*If MMU is not used, this purpose can be ignored
/
/ 9. Set REFRESH[22]=1b and let SDRAM enter self refresh mode/
/ 10. Wait for SDRAM to enter self refresh mode successfully/
/ 11. Set MISCCR[19:17]=111b to protect SDRAM signals (SCLK0,SCLK1 and SCKE)/
/ 12. Set the SLEEP bit of CLKCON to let the system enter sleep mode*/

Before entering str, 1 First fill in the TLB and copy the page table into the TLB to prevent SDRAM from entering the self refresh mode. The virtual address cannot find the physical address through the page table.
2. Put some instructions of SDRAM into Icache so that the cpu can run normally during SDRAM self refresh to sleep. 3. Enter SDRAM into self refresh mode 4 Set the sleep bit of CLKCON to let the system enter sleep mode
Self refresh of SDRAM

The following picture gives an overview of the Linux suspend & resume process. Readers can follow this process to read the kernel source code. For specific instructions, please refer to the following code analysis.

Do the following in user space:

echo "freeze" > /sys/power/state

echo "standby" > /sys/power/state

echo "mem" > /sys/power/state

The execution of suspend will be triggered through sysfs. What actions will echo take at the bottom?
Look at one first
power_ The source code of attr (state) is as follows:

//Located at \ kernel \ power \ power h
#define power_attr(_name) \
static struct kobj_attribute _name##_attr = {	\
	.attr	= {				\
		.name = __stringify(_name),	\
		.mode = 0644,			\
	},					\
	.show	= _name##_show,			\
	.store	= _name##_store,		\
}
===========================
//Will power_ By substituting attr (state), we can define a structure
static struct kobj_attribute state_attr = {	\
	.attr	= {				\
		.name = __stringify(_name),	\
		.mode = 0644,			\
	},					\
	.show	= state_show,			\
	.store	= state_store,		\
}

Then it was found in attr_group structure

static struct attribute * g[] = {
	&state_attr.attr,   //Here's the state_attr
#ifdef CONFIG_PM_TRACE
	&pm_trace_attr.attr,
	&pm_trace_dev_match_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP
	&pm_async_attr.attr,
	&wakeup_count_attr.attr,
#ifdef CONFIG_PM_AUTOSLEEP
	&autosleep_attr.attr,
#endif
#ifdef CONFIG_PM_WAKELOCKS
	&wake_lock_attr.attr,
	&wake_unlock_attr.attr,
#endif
#ifdef CONFIG_PM_DEBUG
	&pm_test_attr.attr,
#endif
#ifdef CONFIG_PM_SLEEP_DEBUG
	&pm_print_times_attr.attr,
#endif
#endif
#ifdef CONFIG_FREEZER
	&pm_freeze_timeout_attr.attr,
#endif
	NULL,
};



static struct attribute_group attr_group = {  //attr_ You can get the attribute in the group
	.attrs = g,
};

static int __init pm_init(void)
{
	int error = pm_start_workqueue();
	if (error)
		return error;
	hibernate_image_size_init();
	hibernate_reserved_size_init();
	power_kobj = kobject_create_and_add("power", NULL);
	if (!power_kobj)
		return -ENOMEM;
	error = sysfs_create_group(power_kobj, &attr_group);
	if (error)
		return error;
	pm_print_times_init();
	return pm_autosleep_init();
}

Here, a state file is created in the power directory, and the corresponding read function is state_show, the write function is state_store, so echo "mem" > / sys / power / state will trigger state_store function.

//Located in kernel \ power \ main c
static ssize_t state_store(struct kobject *kobj, struct kobj_attribute *attr,
			   const char *buf, size_t n)
{
	suspend_state_t state;
	int error;

	error = pm_autosleep_lock();
	if (error)
		return error;

	if (pm_autosleep_state() > PM_SUSPEND_ON) {
		error = -EBUSY;
		goto out;
	}

	state = decode_state(buf, n);
	if (state < PM_SUSPEND_MAX)
		error = pm_suspend(state); [See analysis below]
	else if (state == PM_SUSPEND_MAX)
		error = hibernate();
	else
		error = -EINVAL;

 out:
	pm_autosleep_unlock();
	return error ? error : n;
}

power_attr(state);

power_attr defines an attribute file named state, whose store interface is state_store. After lock ing the autosleep function, the interface parses the buffer (free, standby or mem) passed in by the user and converts it into a state parameter.
The state parameter is of type suspend_state_t. In include \ Linux \ suspend H is the representation of power management status in the kernel. The details are as follows:

typedef int __bitwise suspend_state_t;
 
#define PM_SUSPEND_ON           ((__force suspend_state_t) 0)
#define PM_SUSPEND_FREEZE       ((__force suspend_state_t) 1)
#define PM_SUSPEND_STANDBY      ((__force suspend_state_t) 2)
#define PM_SUSPEND_MEM          ((__force suspend_state_t) 3)
#define PM_SUSPEND_MIN          PM_SUSPEND_FREEZE
#define PM_SUSPEND_MAX          ((__force suspend_state_t) 4)

Let's look at pm_suspend function and

int pm_suspend(suspend_state_t state)
{
	int error;

	if (state <= PM_SUSPEND_ON || state >= PM_SUSPEND_MAX)
		return -EINVAL;

	error = enter_state(state);//[see analysis below]
	if (error) {
		suspend_stats.fail++;
		dpm_save_failed_errno(error);
	} else {
		suspend_stats.success++;
	}
	return error;
}

static int enter_state(suspend_state_t state)
{
        int error;
 
        if (!valid_state(state))  //[see analysis below]
                return -ENODEV;
 
        if (!mutex_trylock(&pm_mutex))
                return -EBUSY;
 
        if (state == PM_SUSPEND_FREEZE)
                freeze_begin();
 
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
        printk("done.\n");
 
        pr_debug("PM: Preparing system for %s sleep\n", pm_states[state]);
        error = suspend_prepare(state);//[see analysis below]
        if (error)
                goto Unlock;
 
        if (suspend_test(TEST_FREEZER))
                goto Finish;
 
        pr_debug("PM: Entering %s sleep\n", pm_states[state]);
        pm_restrict_gfp_mask();
        //[see analysis below]
        error = suspend_devices_and_enter(state);//Put the device into sleep
        pm_restore_gfp_mask();
 
 Finish:
        pr_debug("PM: Finishing wakeup.\n");
        suspend_finish();
 Unlock:
        mutex_unlock(&pm_mutex);
        return error;
}

bool valid_state(suspend_state_t state)
{
        if (state == PM_SUSPEND_FREEZE) {
                return true;
        }
        /*
         * PM_SUSPEND_STANDBY and PM_SUSPEND_MEMORY states need lowlevel
         * support and need to be valid to the lowlevel
         * implementation, no valid callback implies that none are valid.
         */
        return suspend_ops && suspend_ops->valid && suspend_ops->valid(state);
}
//Call valid_state to judge whether the platform supports the power state.
//If it is free, it can be supported without the participation of platform code. It directly returns true. For standby and mem, suspend needs to be called_ The valid of OPS is returned, and the underlying platform code determines whether it supports it.

static int suspend_prepare(suspend_state_t state)
{
	int error;

	if (!sleep_state_supported(state))
		return -EPERM;

	pm_prepare_console();

	error = pm_notifier_call_chain(PM_SUSPEND_PREPARE);//Notify all drivers who care about sleep messages
	if (error)
		goto Finish;

	trace_suspend_resume(TPS("freeze_processes"), 0, true);
	error = suspend_freeze_processes();//Freeze APP and kernel threads
	trace_suspend_resume(TPS("freeze_processes"), 0, false);
	if (!error)
		return 0;

	suspend_stats.failed_freeze++;
	dpm_save_failed_step(SUSPEND_FREEZE);
 Finish:
	pm_notifier_call_chain(PM_POST_SUSPEND);
	pm_restore_console();
	return error;
}

//Put the device into sleep
int suspend_devices_and_enter(suspend_state_t state)
{
        int error;
        bool wakeup = false;
       
        if (need_suspend_ops(state) && !suspend_ops)
                return -ENOSYS;
 
        trace_machine_suspend(state);
        //Check whether the platform code needs to be provided and whether suspend is provided_ ops
        //Call suspend_ The begin callback of OPS (if any) notifies the platform code so that it can handle it accordingly (if necessary). It may fail. You need to jump to Close to perform the recovery operation (suspend_ops - > end).
        if (need_suspend_ops(state) && suspend_ops->begin) {
                error = suspend_ops->begin(state);
                if (error)
                        goto Close;
        }
        suspend_console();//Stop serial port
        ftrace_stop();
        suspend_test_start();
        //dpm_ suspend_ DPM is called in start_ Prepare (state) and dpm_suspend(state) two functions
       /*1.dpm_prepare(state)In, for DPM_ Every device in the list calls device_prepare is the preparation phase
       That is, for each pen involved, call its dev - > PM_ domain->ops->suspend_ Noirq or
	   								                   dev->type->pm->suspend_noirq      or
	   									               dev->class->pm->suspend_noirq     or
	   									               dev->bus->pm->suspend_noirq       or
	   									               dev->driver->pm->suspend_noirq	*/
	   /*2.dpm_suspend(state)In, let all kinds of devices sleep
	   Put the prepared device into dpm_prepared_list, for DPM_ prepared_ Every device in the list calls device_suspend(dev);
							__device_suspend(dev, pm_transition, false);
								For this device, call its dev - > PM_ domain->ops->suspend 	 or
								                   dev->type->pm->suspend       or
								                   dev->class->pm->suspend      or
								                   dev->bus->pm->suspend        or
								                   dev->driver->pm->suspend*/
		
        error = dpm_suspend_start(PMSG_SUSPEND);
        if (error) {
                printk(KERN_ERR "PM: Some devices failed to suspend\n");
               //Note: call dpm_suspend_start, call the - > prepare and - > suspend callback functions of all devices. Suspend requires devices that are normally suspended. suspend device may fail. You need to skip to Recover_platform, execute the recover operation (suspend_ops - > recover).
                goto Recover_platform;
        }
        suspend_test_finish("suspend devices");
        if (suspend_test(TEST_DEVICES))
                goto Recover_platform;
 
        do {
                error = suspend_enter(state, &wakeup);//Put CPU into sleep
                //suspend_enter returns if the reason for the return is not an error and is not a wakeup event. Suspend is called_ Suspend of OPS_ Again callback to check whether it is necessary to suspend again. Under what circumstances do you want to suspend again? It depends on the specific platform.
        } while (!error && !wakeup && need_suspend_ops(state)
                && suspend_ops->suspend_again && suspend_ops->suspend_again());//
 
 Resume_devices:
        suspend_test_start();
        dpm_resume_end(PMSG_RESUME);
        suspend_test_finish("resume devices");
        ftrace_start();
        resume_console();//Reply serial port
 Close:
        if (need_suspend_ops(state) && suspend_ops->end)
                suspend_ops->end();
        trace_machine_suspend(PWR_EVENT_EXIT);
        return error;
 
 Recover_platform:
        if (need_suspend_ops(state) && suspend_ops->recover)
                suspend_ops->recover();
        goto Resume_devices;
}

suspend_ Where is OPS sacred?
We can find his assignment

void suspend_set_ops(const struct platform_suspend_ops *ops)
{
	suspend_state_t i;
	int j = 0;

	lock_system_sleep();

	suspend_ops = ops; //To suspend_ops assignment
	for (i = PM_SUSPEND_MEM; i >= PM_SUSPEND_STANDBY; i--)
		if (valid_state(i)) {
			pm_states[i] = pm_labels[j++];
		} else if (!relative_states) {
			pm_states[i] = NULL;
			j++;
		}

	pm_states[PM_SUSPEND_FREEZE] = pm_labels[j];

	unlock_system_sleep();
}

The ultimate purpose of suspend is to enable the system to enter a recoverable suspended state, and this function can only be completed with the participation of platform related code. Therefore, the kernel PM Core provides a series of callback functions (encapsulated in platform_suspend_ops) to be implemented by platform code (such as arch / arm / Mach XXX / PM. C), and then called by PM Core at an appropriate time. These callback functions include a valid function, which is used to tell the PM Core which states are supported.
For example:
At arch \ arm \ plat sampling \ PM c
s3c_ pm_ In init function
suspend_set_ops(&s3c_pm_ops);
suspend_ops = ops

For example:
static struct pm_ops s3c2410_pm_ops = {
.pm_disk_mode = PM_DISK_FIRMWARE,
.prepare = s3c2410_pm_prepare,
.enter = s3c2410_pm_enter,
.finish = s3c2410_pm_finish,
};
The above are the preparations before suspend. At this time, call suspend_enter interface to enable the system to enter the specified power state. The contents of the interface are as follows:

//Located in kernel \ power \ suspend c
static int suspend_enter(suspend_state_t state, bool *wakeup)
{
	int error;

   //Call suspend_ The prepare callback of OPS (if any) notifies the platform code so that it can do some more processing (if necessary) when it is about to switch states. The callback may fail (the platform code is unexpected). If it fails, you need to jump to the platform_ At finish, call suspend_ The finish callback of OPS performs the recovery operation.
	error = platform_suspend_prepare(state);
	if (error)
		goto Platform_finish;
    /*dpm_suspend_late(state); (drivers/base/power/main.c) 
								Add the dormant device in the above to the dpm_suspended_list linked list, for DPM_ suspended_ Every device in the list calls device_suspend_late(dev, state) to clean up after hibernation.
								   For this device, call its dev - > PM_ domain->ops->suspend_ Late or
										                   dev->type->pm->suspend_late      or
										                   dev->class->pm->suspend_late     or
										                   dev->bus->pm->suspend_late       or
										                   dev->driver->pm->suspend_late   */
	error = dpm_suspend_late(PMSG_SUSPEND);
	if (error) {
		printk(KERN_ERR "PM: late suspend of devices failed\n");
		goto Platform_finish;
	}
	//Call suspend_ Prepare for Ops_ Late callback (if any) to notify the platform code so that it can do some processing at the last minute (if necessary). The callback may fail (the platform code is unexpected). If it fails, you need to jump to the platform_ At wake, call suspend_ The wake callback of OPS executes the resume of device and calls suspend_ The finish callback of OPS performs the recovery operation.
	error = platform_suspend_prepare_late(state);
	if (error)
		goto Devices_early_resume;

	error = dpm_suspend_noirq(PMSG_SUSPEND);
	if (error) {
		printk(KERN_ERR "PM: noirq suspend of devices failed\n");
		goto Platform_early_resume;
	}
	error = platform_suspend_prepare_noirq(state);
	if (error)
		goto Platform_wake;

	if (suspend_test(TEST_PLATFORM))
		goto Platform_wake;

	/*
	 * PM_SUSPEND_FREEZE equals
	 * frozen processes + suspended devices + idle processors.
	 * Thus we should invoke freeze_enter() soon after
	 * all the devices are suspended.
	 */
	if (state == PM_SUSPEND_FREEZE) {
		trace_suspend_resume(TPS("machine_suspend"), state, true);
		freeze_enter();
		trace_suspend_resume(TPS("machine_suspend"), state, false);
		goto Platform_wake;
	}
    //Call disable_nonboot_cpus, disable all non boot CPUs. It will also fail. Just perform the recovery operation.
	error = disable_nonboot_cpus();
	if (error || suspend_test(TEST_CPUS))
		goto Enable_cpus;
    //Call arch_suspend_disable_irqs, turn off global interrupt. If it cannot be closed, it is a bug.
	arch_suspend_disable_irqs();
	BUG_ON(!irqs_disabled());
    //Close the core module
	error = syscore_suspend();
	if (!error) {
		*wakeup = pm_wakeup_pending();
		if (!(suspend_test(TEST_CORE) || *wakeup)) {
			trace_suspend_resume(TPS("machine_suspend"),
				state, true);
			//Call suspend_ Enter callback of OPS to switch states. At this time, the system should have suspended. The platform's enter interface will be called here, and
			//1. Configure wake-up interrupt 2 Set TLB 3 Self refresh ram 4 Set SLEEP 5 of CLKCON Set register return value, etc
			error = suspend_ops->enter(state);
			trace_suspend_resume(TPS("machine_suspend"),
				state, false);
			events_check_enabled = false;
		}
		syscore_resume();//Here it starts to wake up
	}

	arch_suspend_enable_irqs();//Open interrupt
	BUG_ON(irqs_disabled());

 Enable_cpus:
	enable_nonboot_cpus();//Turn on the nonboot cpu after waking up

 Platform_wake:
    //suspend_ops->wake
	platform_resume_noirq(state);
	/*For DPM_ noirq_ For each device in the list, call device_resume_noirq(dev, state);
									For this device, call its dev - > PM_ domain->ops->resume_ Noirq or
										                   dev->type->pm->resume_noirq       or
										                   dev->class->pm->resume_noirq      or
										                   dev->bus->pm->resume_noirq        or
										                   dev->driver->pm->resume_noirq     */
	dpm_resume_noirq(PMSG_RESUME);

 Platform_early_resume:
    
	platform_resume_early(state);

 Devices_early_resume:
 /*For DPM_ late_ early_ For each device in the list, call device_resume_early(dev, state);
									For this device, call its dev - > PM_ domain->ops->resume_ Early or
										                   dev->type->pm->resume_early       or
										                   dev->class->pm->resume_early      or
										                   dev->bus->pm->resume_early        or
										                   dev->driver->pm->resume_early */
	dpm_resume_early(PMSG_RESUME);

 Platform_finish:
	platform_resume_finish(state);
	return error;
}

After waking up, it will return and continue the resume operation, including resume device, start ftrace, resume console and suspend_ Ops - > end, etc.
suspend_ In finish

static void suspend_finish(void)
{
        suspend_thaw_processes();
        pm_notifier_call_chain(PM_POST_SUSPEND);
        pm_restore_console();
}
a)Restore all user space processes and kernel threads app

b)send out suspend Notice of end.

c)take console Switch back to the original.

Calling process of relevant power management functions in the driver:
Sleep: prepare - > suspend - > suspend_ late—>suspend_ noirq
Wake up: resume_noirq—>resume_ early—>resume–>complete
The framework of the whole sleep process is as follows:

------------------------------
state_store (kernel/power/main.c)
	pm_suspend (kernel/power/suspend.c)
		enter_state (kernel/power/suspend.c)
			suspend_prepare (kernel/power/suspend.c)
				pm_prepare_console (kernel/power/console.c)
					pm_notifier_call_chain(PM_SUSPEND_PREPARE); (kernel/power/main.c)  // Notify all drivers who care about sleep messages
					suspend_freeze_processes (kernel/power/power.h) // Freeze APP and kernel threads
			suspend_devices_and_enter (kernel/power/suspend.c) // Put the device into sleep
				suspend_ops->begin  // If the platform related code has a begin function, call it			
				suspend_console (kernel/power/suspend.c)
				dpm_suspend_start(PMSG_SUSPEND); (drivers/base/power/main.c)
					dpm_prepare(state);  (drivers/base/power/main.c)
						about dmp_list Every device in the linked list,All call device_prepare(dev, state);
								For this device,Call its dev->pm_domain->ops->prepare or
								                   dev->type->pm->prepare       or
								                   dev->class->pm->prepare      or
								                   dev->bus->pm->prepare        or
								                   dev->driver->pm->prepare
					dpm_suspend(state); (drivers/base/power/main.c)  // Let all kinds of devices sleep
						about dpm_prepared_list Every device in the linked list,All call device_suspend(dev);
							__device_suspend(dev, pm_transition, false);
								For this device,Call its dev->pm_domain->ops->suspend	or
								                   dev->type->pm->suspend       or
								                   dev->class->pm->suspend      or
								                   dev->bus->pm->suspend        or
								                   dev->driver->pm->suspend

					suspend_enter(state, &wakeup) (kernel/power/suspend.c)
						suspend_ops->prepare // s3c_pm_prepare
						dpm_suspend_end(PMSG_SUSPEND);		 (drivers/base/power/main.c) 
							dpm_suspend_late(state); (drivers/base/power/main.c) 
								about dpm_suspended_list Every device in the linked list,All call device_suspend_late(dev, state);
								   For this device,Call its dev->pm_domain->ops->suspend_late      or
										                   dev->type->pm->suspend_late      or
										                   dev->class->pm->suspend_late     or
										                   dev->bus->pm->suspend_late       or
										                   dev->driver->pm->suspend_late
							dpm_suspend_noirq
								about dpm_late_early_list Every device in the linked list,All call device_suspend_noirq(dev, state);
								   For this device,Call its dev->pm_domain->ops->suspend_noirq      or
										                   dev->type->pm->suspend_noirq      or
										                   dev->class->pm->suspend_noirq     or
										                   dev->bus->pm->suspend_noirq       or
										                   dev->driver->pm->suspend_noirq										                   
						suspend_ops->prepare_late() // 		   
						disable_nonboot_cpus();  	
						arch_suspend_disable_irqs();
						syscore_suspend
						suspend_ops->enter(state);  // s3c_pm_enter (arch\arm\plat-samsung\pm.c)								                   
							......
							pm_cpu_prep // s3c2410_pm_prepare (arch\arm\mach-s3c24xx\pm-s3c2410.c)
								GSTATUS3 = s3c_cpu_resume
								
							......	
							cpu_suspend(0, pm_cpu_sleep); // arch\arm\kernel\sleep.S
								pm_cpu_sleep (arch\arm\mach-s3c24xx\pm-s3c2410.c) // s3c2410_cpu_suspend
									s3c2410_cpu_suspend (arch\arm\mach-s3c24xx\sleep-s3c2410.S)
							The above is the dormancy process		
							===================================
							Let's start the wake-up process
							Key, cause u-boot function, read GSTATUS3, implement s3c_cpu_resume 
							.....
							s3c_pm_restore_core
						syscore_resume
						arch_suspend_enable_irqs
						enable_nonboot_cpus
						suspend_ops->wake
						dpm_resume_start(PMSG_RESUME);	
							dpm_resume_noirq(state);
								about dpm_noirq_list Every device in the linked list,call device_resume_noirq(dev, state);
									For this device,Call its dev->pm_domain->ops->resume_noirq      or
										                   dev->type->pm->resume_noirq       or
										                   dev->class->pm->resume_noirq      or
										                   dev->bus->pm->resume_noirq        or
										                   dev->driver->pm->resume_noirq
							dpm_resume_early(state);
								about dpm_late_early_list Every device in the linked list,call device_resume_early(dev, state);
									For this device,Call its dev->pm_domain->ops->resume_early      or
										                   dev->type->pm->resume_early       or
										                   dev->class->pm->resume_early      or
										                   dev->bus->pm->resume_early        or
										                   dev->driver->pm->resume_early
							suspend_ops->finish()
								s3c_pm_finish
											
				dpm_resume_end(PMSG_RESUME);
				resume_console();
			suspend_finish();
				suspend_thaw_processes();
				pm_notifier_call_chain(PM_POST_SUSPEND);
				pm_restore_console();
			//Return to user space	

Keywords: Linux

Added by n8r0x on Fri, 14 Jan 2022 17:28:29 +0200