RK3399 Development Board Sleep, Shutdown Function

RK3399 development board

Article directory

Power button

~Short press to sleep and long press to power off

shutdown command

~poweroff command to shutdown


The OK3399-C development board is designed with the RK3399 main CPU chip and supports the power button sleep wake-up and shutdown functions on the bottom plate. The Linux command line can also be shut down by commands. The following two methods are briefly analyzed.

Power button

The sleep wake-up function of the Power button is generally related to the PMIC chip and the CPU. On the hardware, the button will be connected to the PWRON pin of the PMIC and the corresponding pin of the CPU.

Let's first look at the response mechanism of the power button on the PMIC side. After the POWER button is pressed, after the TdbPWRONF time, the INT pin becomes a low level and an interrupt is triggered. The kernel driver responds to the interrupt and executes the sleep program.

If PWRON continues to remain low beyond TdPWRONLP, the PMIC will respond and the RK3399 SBC will shut down.

RK3399 development board

Short press to sleep and long press to power off

Log information:

[   10.250531] PM: Syncing filesystems ... done.
[   10.255148] test message.            //Added test information
[   10.256842] Freezing user space processes ... (elapsed 0.003 seconds) done.
[   10.260770] Freezing remaining freezable tasks ... (elapsed 0.001 seconds) done. 
[   10.263308] Suspending console(s) (use no_console_suspend to debug)
INFO:    sleep mode config[0xde]:
INFO:           AP_PWROFF
INFO:           SLP_ARMPD 
INFO:           SLP_PLLPD
INFO:           DDR_RET
INFO:           SLP_CENTER_PD
INFO:    wakeup source config[0x804]:
INFO:           GPIO interrupt can wakeup system
INFO:           PWM interrupt can wakeup system
INFO:    PWM CONFIG[0x4]:
INFO:           PWM: PWM2D_REGULATOR_EN
INFO:    APIOS info[0x0]:
INFO:           not config
INFO:    GPIO POWER INFO:
INFO:           GPIO1_C1
INFO:           GPIO1_B6
INFO:    PMU_MODE_CONG: 0x1477bf51

Involving driver files

kernel/kernel/power/suspend.c
/**
 * enter_state - Do common work needed to enter system sleep state.
 * @state: System sleep state to enter.
 *
 * Make sure that no one else is trying to put the system into a sleep state.
 * Fail if that's not the case.  Otherwise, prepare for system suspend, make the
 * system enter the given sleep state and clean up after wakeup.
 */
static int enter_state(suspend_state_t state)
{
        int error;
        trace_suspend_resume(TPS("suspend_enter"), state, true);
        if (state == PM_SUSPEND_FREEZE) {
#ifdef CONFIG_PM_DEBUG
                if (pm_test_level != TEST_NONE && pm_test_level <= TEST_CPUS) {
                        pr_warning("PM: Unsupported test mode for suspend to idle,"
                                   "please choose none/freezer/devices/platform.\n");
                        return -EAGAIN;
                }
#endif
        } else if (!valid_state(state)) {
                return -EINVAL;
        }
        if (!mutex_trylock(&pm_mutex))
                return -EBUSY;
        if (state == PM_SUSPEND_FREEZE)
                freeze_begin();
#ifndef CONFIG_SUSPEND_SKIP_SYNC
        trace_suspend_resume(TPS("sync_filesystems"), 0, true);
        printk(KERN_INFO "PM: Syncing filesystems ... ");
        sys_sync();
        printk("done.\n");
        trace_suspend_resume(TPS("sync_filesystems"), 0, false);
#endif
        pr_debug("PM: Preparing system for sleep (%s)\n", pm_states[state]);
        pm_suspend_clear_flags();
        error = suspend_prepare(state);
        if (error)
                goto Unlock;
        if (suspend_test(TEST_FREEZER))
                goto Finish;
        trace_suspend_resume(TPS("suspend_enter"), state, false);
        pr_debug("PM: Suspending system (%s)\n", pm_states[state]);
        pm_restrict_gfp_mask();
        error = suspend_devices_and_enter(state);
        pm_restore_gfp_mask();
 Finish:
        pr_debug("PM: Finishing wakeup.\n");
        suspend_finish();
 Unlock:
        mutex_unlock(&pm_mutex);
        return error;
}

It can be seen from the code that the sleep and shutdown triggered by the Power button will execute the sys_sync function to save system data, which is very different from the abnormal shutdown caused by a sudden power failure.

Shutdown Command

The shutdown commands under Linux on the RK3399 platform include shutdown, halt, poweroff commands, etc.

Poweroff command to shut down

Log information:

[root@rk3399:/]# poweroff
[root@rk3399:/]# stop finishedStopping input-event-daemon: done
stop auto-reboot finished
Stopping dnsmasq: OK
Stopping vsftpd: stopped vsftpd (pid 1072)
OK
[   20.099392] [BT_RFKILL]: bt shut off power
[   20.132245] configfs-gadget gadget: unbind function 'Function FS Gadget'/ffffffc07b025a38
Stopping sshd: OK
Stopping lighttpd: OK
Gracefully shutting down php-fpm . done
Stopping dhcpcd...
stopped /sbin/dhcpcd (pid 924)
killall: rkisp_3A_server: no process killed
Stopping network: OK
stop finishedStopping system message bus: done
Saving random seed... done.
Stopping logging: OK
umount: can't remount adb read-only
umount: devtmpfs busy - remounted read-only
[   21.589884] EXT4-fs (mmcblk2p8): re-mounted. Opts: (null)
The system is going down NOW! 
Sent SIGTERM to all processes
Sent SIGKILL to all processes
Requesting system poweroff
[   23.597578] cpu cpu4: min=816000, max=816000
[   23.598572] cpu cpu0: min=816000, max=816000
[   23.669985] I : [File] : drivers/gpu/arm/midgard_for_linux/platform/rk/mali_kbase_config_rk.c; [Line] : 274; [Func] : kbase_platform_rk_shutdown(); to make vdd_gpu enabled for turning off pd_gpu in pm_framework.
[   23.671701] rk-vcodec ff660000.rkvdec: shutdown
[   23.672132] rk-vcodec ff650000.vpu_service: shutdown
[   23.673046] rk808 0-001b: System power off
[   23.673419] rk808 0-001b: test message        //Added test information
[root@rk3399:/]# poweroff --help
BusyBox v1.27.2 (2020-03-19 09:39:13 UTC) multi-call binary.
Usage: poweroff [-d DELAY] [-n] [-f]
Halt and shut off power
        -d SEC  Delay interval
        -n      Do not sync
        -f      Force (don't go through init)

What the Poweroff command does can be seen from the printed information. In fact, it can be divided into two parts. One is to configure the system, stop the current service, and save the data. The second is to call the corresponding interface of the power management driver to complete the power configuration, and the RK3399 motherboard will be shut down.

Involving driver files

kernel/drivers/mfd/rk808.c
static void rk808_syscore_shutdown(void)
{
        int ret;
        struct rk808 *rk808 = i2c_get_clientdata(rk808_i2c_client);
        if (!rk808) {
                dev_warn(&rk808_i2c_client->dev,
                         "have no rk808, so do nothing here\n");
                return;
        }
        /* close rtc int when power off */
        regmap_update_bits(rk808->regmap,
                           RK808_INT_STS_MSK_REG1,
                           (0x3 << 5), (0x3 << 5));
        regmap_update_bits(rk808->regmap,
                           RK808_RTC_INT_REG,
                           (0x3 << 2), (0x0 << 2));
        /*
         * For PMIC that power off supplies by write register via i2c bus,
         * it's better to do power off at syscore shutdown here.
         *
         * Because when run to kernel's "pm_power_off" call, i2c may has
         * been stopped or PMIC may not be able to get i2c transfer while
         * there are too many devices are competiting.
         */
         if (system_state == SYSTEM_POWER_OFF) {
                /* power off supplies ! */
                if (pm_shutdown) {
                        dev_info(&rk808_i2c_client->dev, "System power off\n");
                        ret = pm_shutdown(rk808->regmap);
                        if (ret)
                                dev_err(&rk808_i2c_client->dev,
                                        "System power off error!\n");
                        mdelay(10);
                        dev_info(&rk808_i2c_client->dev,
                                 "Cpu should never reach here, stop!\n");
                        while (1)
                                ;
                }
        }
}
#define DEV_OFF_RST     BIT(3)
static int rk808_shutdown(struct regmap *regmap)
{
        int ret;
        ret = regmap_update_bits(regmap,
                                 RK808_DEVCTRL_REG,
                                 DEV_OFF_RST, DEV_OFF_RST);
        return ret;
}
 

The finally called rk808shutdown interface function writes DEVOFF_RST to the RK808DEVCTRLREG register, triggering the shutdown of the RK3399 motherboard.