T9632 待机led 控制

当待机流程就会完成一些硬件驱动的现场操作,灯也是如此。
kernel\fusion\4.9\kernel\reboot.c

/*
 * 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 (MP_PLATFORM_PM == 0)
	if ((cmd == LINUX_REBOOT_CMD_POWER_OFF) && !pm_power_off)
		cmd = LINUX_REBOOT_CMD_HALT;
#endif

	mutex_lock(&reboot_mutex);
	switch (cmd) {
	case LINUX_REBOOT_CMD_RESTART:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
		life_cycle_set_boot_reason(WARMBOOT_BY_SW);
#endif
		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:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
		life_cycle_set_shutdown_reason(SHUTDOWN_BY_SW);
#endif
		kernel_power_off();
		do_exit(0);
		break;

	case LINUX_REBOOT_CMD_RESTART2:
#ifdef CONFIG_MP_AMAZON_SIGN_OF_LIFE
		life_cycle_set_boot_reason(WARMBOOT_BY_SW);
#endif
		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(&reboot_mutex);
	return ret;
}

当收到上层reboot 指令时,根据不同的cmd 来决定是关机,重启,挂起等等。

/*
 * Commands accepted by the _reboot() system call.
 *
 * RESTART     Restart system using default command and mode.
 * HALT        Stop OS and give system control to ROM monitor, if any.
 * CAD_ON      Ctrl-Alt-Del sequence causes RESTART command.
 * CAD_OFF     Ctrl-Alt-Del sequence sends SIGINT to init task.
 * POWER_OFF   Stop OS and remove all power from system, if possible.
 * RESTART2    Restart system using given command string.
 * SW_SUSPEND  Suspend system using software suspend if compiled in.
 * KEXEC       Restart system using a previously loaded Linux kernel
 */

#define	LINUX_REBOOT_CMD_RESTART	0x01234567
#define	LINUX_REBOOT_CMD_HALT		0xCDEF0123
#define	LINUX_REBOOT_CMD_CAD_ON		0x89ABCDEF
#define	LINUX_REBOOT_CMD_CAD_OFF	0x00000000
#define	LINUX_REBOOT_CMD_POWER_OFF	0x4321FEDC
#define	LINUX_REBOOT_CMD_RESTART2	0xA1B2C3D4
#define	LINUX_REBOOT_CMD_SW_SUSPEND	0xD000FCE2
#define	LINUX_REBOOT_CMD_KEXEC		0x45584543

本篇我们关注关机流程,收到 cmd “LINUX_REBOOT_CMD_POWER_OFF ” 时,就会进入kernel_power_off.

void kernel_power_off(void)
{
#if defined(CONFIG_CPU_FREQ_DEFAULT_GOV_ONDEMAND) || defined(CONFIG_CPU_FREQ_DEFAULT_GOV_INTERACTIVE) || defined(CONFIG_CPU_FREQ_DEFAULT_GOV_SCHEDUTIL)
#if defined(CONFIG_MSTAR_CPU_CLUSTER_CALIBRATING)
        atomic_set(&disable_dvfs_reboot, 1);
#endif
#endif

#if defined(CONFIG_MSTAR_PM)
	check_boot_mode = 0;
#endif

	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();
#ifdef CONFIG_MSTAR_MBX
#if (MP_PLATFORM_PM == 1)
	mstar_str_notifypmmaxcnt_off();
#endif
#endif
}

pm_power_off_prepare 做关机前PM的预处理,
migrate_to_reboot_cpu
syscore_shutdown 调用各个驱动注册的shutdown 回调函数
machine_power_off 停止CPU任务

/*
 * Power-off simply requires that the secondary CPUs stop performing any
 * activity (executing tasks, handling interrupts). smp_send_stop()
 * achieves this. When the system power is turned off, it will take all CPUs
 * with it.
 */
void machine_power_off(void)
{
	local_irq_disable();
	smp_send_stop();
	if (pm_power_off)
		pm_power_off();
}

pm_power_off PM开始断电。

下面我们主要看和MTK平台相关的部分,其它kernel 的暂不关注。
pm_power_off_prepare 和 pm_power_off 都是在mstar2\hal\m7332\cpu\arm\pm.c 里面注册


static void mstar_pm_power_off_prepare(void)
{
    printk("mstar set pm power off param\n");
    MDrv_PM_Reboot(E_PM_STATE_POWER_OFF_PRE);
    mstar_sleep_cur_cpu_flush();
}

static void mstar_pm_power_off(void)
{
    printk("mstar pm power down\n");
    MDrv_PM_Reboot(E_PM_STATE_POWER_OFF);
    _mstar_str_enter_tee(NULL);
}

static int __init mstar_pm_init(void)
{
	pm_power_off_prepare = mstar_pm_power_off_prepare;
	pm_power_off = mstar_pm_power_off;
	/* set operation function of suspend */
	suspend_set_ops(&mstar_pm_ops);
	return 0;
}

mstar_pm_power_off_prepare 和 mstar_pm_power_off 调用 MDrv_PM_Reboot 发送了E_PM_STATE_POWER_OFF_PRE, E_PM_STATE_POWER_OFF 两个cmd,但是E_PM_STATE_POWER_OFF没有做处理。

PM_Result MDrv_PM_Reboot(PM_STATE eState)
{
    PM_Result eRet = E_PM_OK;

    switch (eState)
    {
        case E_PM_STATE_REBOOT_NOTIFY:
            if (!is_mstar_str())
            {
                // MDrv_PM_Mapping() via mi notify callback.
                eRet &= _MDrv_PM_Store_Fw();
            }
            break;

        case E_PM_STATE_POWER_OFF_PRE:
            if (!is_mstar_str())
            {
                /* TODO: Remove. */
                _MDrv_PM_Set_Ir();
                #ifdef CONFIG_MSTAR_SYSFS_BACKLIGHT
                MDrv_PM_TurnoffBacklight();
                #endif

                #if (CONFIG_MSTAR_GPIO)
                _MDrv_PM_Set_Led();
                #endif

                // TODO: Remove patch for reboot recovery case.
                _MDrv_PM_Patch_Mapping();

                _MDrv_PM_Set_Mode(PM_DC_MODE);
                _MDrv_PM_Flush_Config();
                eRet &= _MDrv_PM_BringUp();
            }
            break;

        default:
            eRet = E_PM_FAIL;
            break;
    }

    return eRet;
}

接着 _MDrv_PM_Set_Led 开始控制灯

#if (CONFIG_MSTAR_GPIO)
static void _MDrv_PM_Set_Led(void)
{
    u8 u8LedPad = 0;

    u8LedPad = MDrv_PM_Get_PowerOffLed();
    printk("Get LedPadNum = %d.\n", u8LedPad);
    if (u8LedPad != PM_SOURCE_DISABLE)
    {
        printk("======= set red led on (set PAD_PWM_PM to High) ========\n");
        MDrv_GPIO_Set_High(u8LedPad);
    }
}
#endif

而这个LedPad 是在PM 注册时设置

static int _mstar_drv_pm_probe(struct platform_device *pdev)
{
    int retval = 0;
    U8 u8LedPad = 0xFF;
    struct device_node *node = pdev->dev.of_node;
    const struct of_device_id *match = of_match_device(of_match_ptr(mstar_pm_of_device_ids), &pdev->dev);
    if (!(pdev->name) || strcmp(pdev->name, MDRV_PM_DD_NAME) || (pdev->id != 0))
    {
        retval = -ENXIO;
    }

    if (match != NULL)
    {
        if (of_property_read_u8(node, "poweroff_led", &u8LedPad) >= 0)
        {
            if (u8LedPad != 0xFF)
            {
                MDrv_PM_Set_PowerOffLed(u8LedPad);
            }
        }
    }

    return retval;
}

poweroff_led 即是从设备树中配置。
在m7332_an.dts 中配置mstar-pm 驱动的poweroff_led 属性

        mstar-pm {
                compatible = "mstar-pm";
                poweroff_led = /bits/ 8 <4>;
        };

其中4 为 PAD_PWM_PM 脚,在mhal_gpio_reg.h 文件中定义

#define PAD_PWM_PM 4

在现在框架里面根据,在dts 配置上Led 的pin 即可。也可以在mstar_pm_power_off_prepare或者mstar_pm_power_off自行添加。

拓展
system\core\reboot\reboot.c 收到reboot -p 指令时,会设置系统属性"sys.powerctl", system\core\init\init.cpp property_changed 检测到属性变化时,会处理

bool HandlePowerctlMessage(const std::string& command) {
    unsigned int cmd = 0;
    std::vector<std::string> cmd_params = android::base::Split(command, ",");
    std::string reboot_target = "";
    bool run_fsck = false;
    bool command_invalid = false;

    if (cmd_params.size() > 3) {
        command_invalid = true;
    } else if (cmd_params[0] == "shutdown") {
        cmd = ANDROID_RB_POWEROFF;
        if (cmd_params.size() == 2 && cmd_params[1] == "userrequested") {
            // The shutdown reason is PowerManager.SHUTDOWN_USER_REQUESTED.
            // Run fsck once the file system is remounted in read-only mode.
            run_fsck = true;
        }
    } else if (cmd_params[0] == "reboot") {
        cmd = ANDROID_RB_RESTART2;
        if (cmd_params.size() >= 2) {
            reboot_target = cmd_params[1];
            // When rebooting to the bootloader notify the bootloader writing
            // also the BCB.
            if (reboot_target == "bootloader") {
                std::string err;
                if (!write_reboot_bootloader(&err)) {
                    LOG(ERROR) << "reboot-bootloader: Error writing "
                                  "bootloader_message: "
                               << err;
                }
            }
            // If there is an additional bootloader parameter, pass it along
            if (cmd_params.size() == 3) {
                reboot_target += "," + cmd_params[2];
            }
        }
    } else if (command == "thermal-shutdown") {  // no additional parameter allowed
        cmd = ANDROID_RB_THERMOFF;
    } else {
        command_invalid = true;
    }
    if (command_invalid) {
        LOG(ERROR) << "powerctl: unrecognized command '" << command << "'";
        return false;
    }

    DoReboot(cmd, command, reboot_target, run_fsck);
    return true;
}

doReboot 进一步处理

void DoReboot(unsigned int cmd, const std::string& reason, const std::string& rebootTarget,
              bool runFsck) {
    Timer t;
    LOG(INFO) << "Reboot start, reason: " << reason << ", rebootTarget: " << rebootTarget;

    android::base::WriteStringToFile(StringPrintf("%s\n", reason.c_str()), LAST_REBOOT_REASON_FILE,
                                     S_IRUSR | S_IWUSR, AID_SYSTEM, AID_SYSTEM);

    if (cmd == ANDROID_RB_THERMOFF) {  // do not wait if it is thermal
        DoThermalOff();
        abort();
    }

    constexpr unsigned int shutdownTimeoutDefault = 6;
    unsigned int shutdownTimeout = shutdownTimeoutDefault;
    if (SHUTDOWN_ZERO_TIMEOUT) {  // eng build
        shutdownTimeout = 0;
    } else {
        shutdownTimeout =
            android::base::GetUintProperty("ro.build.shutdown_timeout", shutdownTimeoutDefault);
    }
    LOG(INFO) << "Shutdown timeout: " << shutdownTimeout;

    // keep debugging tools until non critical ones are all gone.
    const std::set<std::string> kill_after_apps{"tombstoned", "logd", "adbd"};
    // watchdogd is a vendor specific component but should be alive to complete shutdown safely.
    const std::set<std::string> to_starts{"watchdogd", "vold", "ueventd"};
    ServiceManager::GetInstance().ForEachService([&kill_after_apps, &to_starts](Service* s) {
        if (kill_after_apps.count(s->name())) {
            s->SetShutdownCritical();
        } else if (to_starts.count(s->name())) {
            s->Start();
            s->SetShutdownCritical();
        }
    });

    Service* bootAnim = ServiceManager::GetInstance().FindServiceByName("bootanim");
    Service* surfaceFlinger = ServiceManager::GetInstance().FindServiceByName("surfaceflinger");
    if (bootAnim != nullptr && surfaceFlinger != nullptr && surfaceFlinger->IsRunning()) {
        ServiceManager::GetInstance().ForEachServiceInClass("animation", [](Service* s) {
            s->SetShutdownCritical();  // will not check animation class separately
        });
    }

    // optional shutdown step
    // 1. terminate all services except shutdown critical ones. wait for delay to finish
    if (shutdownTimeout > 0) {
        LOG(INFO) << "terminating init services";

        // Ask all services to terminate except shutdown critical ones.
        ServiceManager::GetInstance().ForEachService([](Service* s) {
            if (!s->IsShutdownCritical()) s->Terminate();
        });

        int service_count = 0;
        // Up to half as long as shutdownTimeout or 3 seconds, whichever is lower.
        unsigned int terminationWaitTimeout = std::min<unsigned int>((shutdownTimeout + 1) / 2, 3);
        while (t.duration_s() < terminationWaitTimeout) {
            ServiceManager::GetInstance().ReapAnyOutstandingChildren();

            service_count = 0;
            ServiceManager::GetInstance().ForEachService([&service_count](Service* s) {
                // Count the number of services running except shutdown critical.
                // Exclude the console as it will ignore the SIGTERM signal
                // and not exit.
                // Note: SVC_CONSOLE actually means "requires console" but
                // it is only used by the shell.
                if (!s->IsShutdownCritical() && s->pid() != 0 && (s->flags() & SVC_CONSOLE) == 0) {
                    service_count++;
                }
            });

            if (service_count == 0) {
                // All terminable services terminated. We can exit early.
                break;
            }

            // Wait a bit before recounting the number or running services.
            std::this_thread::sleep_for(50ms);
        }
        LOG(INFO) << "Terminating running services took " << t
                  << " with remaining services:" << service_count;
    }

    // minimum safety steps before restarting
    // 2. kill all services except ones that are necessary for the shutdown sequence.
    ServiceManager::GetInstance().ForEachService([](Service* s) {
        if (!s->IsShutdownCritical()) s->Stop();
    });
    ServiceManager::GetInstance().ReapAnyOutstandingChildren();

    // 3. send volume shutdown to vold
    Service* voldService = ServiceManager::GetInstance().FindServiceByName("vold");
    if (voldService != nullptr && voldService->IsRunning()) {
        ShutdownVold();
        voldService->Stop();
    } else {
        LOG(INFO) << "vold not running, skipping vold shutdown";
    }
    // logcat stopped here
    ServiceManager::GetInstance().ForEachService([&kill_after_apps](Service* s) {
        if (kill_after_apps.count(s->name())) s->Stop();
    });
    // 4. sync, try umount, and optionally run fsck for user shutdown
    sync();
    UmountStat stat = TryUmountAndFsck(runFsck, shutdownTimeout * 1000 - t.duration_ms());
    // Follow what linux shutdown is doing: one more sync with little bit delay
    sync();
    std::this_thread::sleep_for(100ms);
    LogShutdownTime(stat, &t);
    // Reboot regardless of umount status. If umount fails, fsck after reboot will fix it.
    RebootSystem(cmd, rebootTarget);
    abort();
}

系统关闭一些上层服务后,会进入内核部分

static void __attribute__((noreturn))
RebootSystem(unsigned int cmd, const std::string& rebootTarget) {
    LOG(INFO) << "Reboot ending, jumping to kernel";
    switch (cmd) {
        case ANDROID_RB_POWEROFF:
            reboot(RB_POWER_OFF);
            break;

        case ANDROID_RB_RESTART2:
            syscall(__NR_reboot, LINUX_REBOOT_MAGIC1, LINUX_REBOOT_MAGIC2,
                    LINUX_REBOOT_CMD_RESTART2, rebootTarget.c_str());
            break;

        case ANDROID_RB_THERMOFF:
            reboot(RB_POWER_OFF);
            break;
    }
    // In normal case, reboot should not return.
    PLOG(FATAL) << "reboot call returned";
    abort();
}

reboot(RB_POWER_OFF); 其中RB_POWER_OFF在libc 库里面定义的值为#define RB_POWER_OFF 0x4321fedc
与内核LINUX_REBOOT_CMD_POWER_OFF 是同一个cmd,接着会进入内核的SYSCALL_DEFINE4(reboot, int, magic1, int, magic2, unsigned int, cmd, void __user *, arg) 函数。

  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值