poweroff, reboot流程

poweroff /halt /reboot操作通常由用户空间的systemd或其他初始化系统通过sys_reboot()系统调用触发

 sys_reboot() 在内核中定义,通常位于kernel/reboot.c文件中。当传递特定的magic值如 LINUX_REBOOT_CMD_POWER_OFF时,内核会执行关机并尝试触发硬件层面的电源关闭

/*
 * Reboot system call: for obvious reasons only root may call it,
 * and even root needs to set up some magic numbers in the registers
 * so that some mistake won't make this reboot the whole machine.
 * You can also set the meaning of the ctrl-alt-del-key here.
 *
 * reboot doesn't sync: do that yourself before calling this.
 */
SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd,
		void __user *, arg)
{
	struct pid_namespace *pid_ns = task_active_pid_ns(current);
	char buffer[256];
	int ret = 0;

	/* We only trust the superuser with rebooting the system. */
	if (!ns_capable(pid_ns->user_ns, CAP_SYS_BOOT))
		return -EPERM;

	/* For safety, we require "magic" arguments. */
	if (magic1 != LINUX_REBOOT_MAGIC1 ||
			(magic2 != LINUX_REBOOT_MAGIC2 &&
			magic2 != LINUX_REBOOT_MAGIC2A &&
			magic2 != LINUX_REBOOT_MAGIC2B &&
			magic2 != LINUX_REBOOT_MAGIC2C))
		return -EINVAL;

	/*
	 * If pid namespaces are enabled and the current task is in a child
	 * pid_namespace, the command is handled by reboot_pid_ns() which will
	 * call do_exit().
	 */
	ret = reboot_pid_ns(pid_ns, cmd);
	if (ret)
		return ret;

	/* Instead of trying to make the power_off code look like
	 * halt when pm_power_off is not set do it the easy way.
	 */
	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;

	mutex_lock(&system_transition_mutex);
	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART:
		kernel_restart(NULL);
		break;

	case LINUX_REBOOT_CMD_CAD_ON:
		C_A_D = 1;
		break;

	case LINUX_REBOOT_CMD_CAD_OFF:
		C_A_D = 0;
		break;

	case LINUX_REBOOT_CMD_HALT:
		kernel_halt();
		do_exit(0);
		panic("cannot halt");

	case LINUX_REBOOT_CMD_POWER_OFF:
		kernel_power_off();
		do_exit(0);
		break;

	case LINUX_REBOOT_CMD_RESTART2:
		ret = strncpy_from_user(&buffer[0], arg, sizeof(buffer) - 1);
		if (ret < 0) {
			ret = -EFAULT;
			break;
		}
		buffer[sizeof(buffer) - 1] = '\0';

		kernel_restart(buffer);
		break;

#ifdef CONFIG_KEXEC_CORE
	case LINUX_REBOOT_CMD_KEXEC:
		ret = kernel_kexec();
		break;
#endif

#ifdef CONFIG_HIBERNATION
	case LINUX_REBOOT_CMD_SW_SUSPEND:
		ret = hibernate();
		break;
#endif

	default:
		ret = -EINVAL;
		break;
	}
	mutex_unlock(&system_transition_mutex);
	return ret;
}

poweroff流程,pm_power_off函数被各平台赋值到具体函数,比如通过操作PMIC来实现关机

void kernel_power_off(void)
{
	kernel_shutdown_prepare(SYSTEM_POWER_OFF);
	if (pm_power_off_prepare)
		pm_power_off_prepare();
	migrate_to_reboot_cpu();
	syscore_shutdown();
	pr_emerg("Power down\n");
	kmsg_dump(KMSG_DUMP_POWEROFF);
	machine_power_off();
}
void machine_power_off(void)
{
	local_irq_disable();
	smp_send_stop();

	if (pm_power_off)
		pm_power_off();
}
void machine_power_off(void)
{
	local_irq_disable();
	smp_send_stop();

	if (pm_power_off)
		pm_power_off();
}
pm_power_off = pm802_sw_poweroff;
static int __pm802_sw_poweroff(void)
{
        u32 val;
        int ret = 0;
        struct pm80x_chip *chip = pm80x_chip_g;

        if (!chip) {
                WARN(1, "__pm802_sw_poweroff: chip is NULL\n");
                return -EINVAL;
        }

        /* clear fault wakeup */
        ret = pmic_rw_reg(PM802_RTC_MISC5, (0x1 << 3), 0x0);
        if (ret < 0) {
                pr_err("%s, set PM803_RTC_MISC5 fail\n", __func__);
                return ret;
        }

        if (CHIP_PM802S_ID_A0 == chip->chip_id
                || CHIP_PM802S_ID_A1 == chip->chip_id
                || CHIP_PM802S_ID_A2 == chip->chip_id)
                val = 0x3;
        else
                val = 0x1;
        /* discharge timer should be set greater than or equal to 1s */
        ret = pmic_rw_reg(PM802_RTC_MISC2, 0xf, val);
        if (ret < 0) {
                pr_err("%s, set PM802_RTC_MISC2 fail\n", __func__);
                return ret;
        }

        ret = pmic_rw_reg(PM802_RTC_MISC4, (0x1 << 1)+1, 0x0);
        if (ret < 0) {
                pr_err("%s, set PM802_RTC_MISC4 fail\n", __func__);
                return ret;
        }

        ret = pmic_rw_reg(PM802_WAKEUP1, PM802_SW_PDOWN, PM802_SW_PDOWN);
        if (ret < 0)
                pr_err("%s, turn off power fail\n", __func__);

        return ret;
}

reboot流程,arm_pm_restart函数可能被具体平台赋值到具体函数,比如操作WDT实现重启

void kernel_restart(char *cmd)
{
	kernel_restart_prepare(cmd);
	migrate_to_reboot_cpu();
	syscore_shutdown();
	if (!cmd)
		pr_emerg("Restarting system\n");
	else
		pr_emerg("Restarting system with command '%s'\n", cmd);
	kmsg_dump(KMSG_DUMP_RESTART);
	machine_restart(cmd);
}
void machine_restart(char *cmd)
{
	local_irq_disable();
	smp_send_stop();

	if (arm_pm_restart)
		arm_pm_restart(reboot_mode, cmd);
	else
		do_kernel_restart(cmd);

	/* Give a grace period for failure to restart of 1s */
	mdelay(1000);

	/* Whoops - the platform was unable to reboot. Tell the user! */
	printk("Reboot failed -- System halted\n");
	while (1);
}

arm_pm_restart = do_pxa_reset;
static void do_pxa_reset(enum reboot_mode mode, const char *cmd)
{
        u32 backup;
        int i;

        if (cmd && (!strcmp(cmd, "recovery")
                || !strcmp(cmd, "bootloader") || !strcmp(cmd, "boot")
                || !strcmp(cmd, "product") || !strcmp(cmd, "prod")
                || !strcmp(cmd, "fastboot") || !strcmp(cmd, "fast"))) {
                for (i = 0, backup = 0; i < 4; i++) {
                        backup <<= 8;
                        backup |= *(cmd + i);
                }
                do {
                        writel(backup, rtc_br0_reg);
                } while (readl(rtc_br0_reg) != backup);
        }

        pxa_wdt_reset(wdt_base, mpmu_base);

        /* Give a grace period for failure to restart of 1s */
        mdelay(1000);
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值