通过引脚来控制整版电源,控制方式如下图:
1) GPIO_19 做开关机按键检测
2) GPIO6_31做电源的控制IO
开机思路:当按下按键后mos4459的第四个引脚被拉低->管子导通->整版上电->系统启动->加载内核->内核中编写 驱动把GPIO6_31拉高->npn三极管8050导通->第四个IO继续被拉低,此时就可以松开送。大概就三四秒钟。
关机思路:开机后长按GPIO_19按键,这个按键会持续向上层发生关机信号,android会弹出关机提示,然后系统会一层层的关闭,最后切断电源即控制GPIO6_31拉低。
先讲下关机的实现,先看一个函数,Kernel_imx/kernel/reboot.c中:
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(&reboot_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
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;
}
当系统接收到一个power信号后就会调用该函数,信号的如何产生我们等会再说,这个函数中有段关键语句:
/* 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;
我们发生过来的cmd是power_off但是如果没有设置pm_power_off这个函数指针则会变成cmd_halt,来看看这个命令分别做了什么:
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;
void kernel_halt(void)
{
kernel_shutdown_prepare(SYSTEM_HALT);
migrate_to_reboot_cpu();
syscore_shutdown();
pr_emerg("System halted\n");
kmsg_dump(KMSG_DUMP_HALT);
machine_halt();
}
EXPORT_SYMBOL_GPL(kernel_halt);
/**
* kernel_power_off - power_off the system
*
* Shutdown everything and perform a clean system power_off.
*/
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();
}
kernel_halt主要是调用关闭系统,停止一些次cpu上的任务。
kernel_power_off就比较灵活给了一个函数指针供用户(驱动)调用:
void machine_power_off(void)
{
local_irq_disable();
smp_send_stop();
if (pm_power_off)
pm_power_off();
}
所以我们想实现控制三极管切换电源只要实现这个函数指针就可以了。
现在修改一个驱动,该驱动在driver/power/reset/gpio-poweroff.c
/*
* Toggles a GPIO pin to power down a device
*
* Jamie Lentin
* Andrew Lunn
*
* Copyright (C) 2012 Jamie Lentin
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
*/
#include
#include
#include
#include
#include
#include
#include
/*
* Hold configuration here, cannot be more than one instance of the driver
* since pm_power_off itself is global.
*/
static struct gpio_desc *reset_gpio;
static void gpio_poweroff_do_poweroff(void)
{
BUG_ON(!reset_gpio);
/* drive it active, also inactive->active edge */
gpiod_direction_output(reset_gpio, 1);
mdelay(100);
/* drive inactive, also active->inactive edge */
gpiod_set_value(reset_gpio, 0);
WARN_ON(1);
}
static int gpio_poweroff_probe(struct platform_device *pdev)
{
bool input = false;
/* If a pm_power_off function has already been added, leave it alone */
if (pm_power_off != NULL) {
dev_err(&pdev->dev,
"%s: pm_power_off function already registered",
__func__);
return -EBUSY;
}
reset_gpio = devm_gpiod_get(&pdev->dev, NULL);
if (IS_ERR(reset_gpio))
return PTR_ERR(reset_gpio);
// input = of_property_read_bool(pdev->dev.of_node, "input");
if (input) {
if (gpiod_direction_input(reset_gpio)) {
dev_err(&pdev->dev,
"Could not set direction of reset GPIO to input\n");
return -ENODEV;
}
} else {
if (gpiod_direction_output(reset_gpio, 0)) {
dev_err(&pdev->dev,
"Could not set direction of reset GPIO\n");
return -ENODEV;
}
}
pm_power_off = &gpio_poweroff_do_poweroff;
return 0;
}
static int gpio_poweroff_remove(struct platform_device *pdev)
{
if (pm_power_off == &gpio_poweroff_do_poweroff)
pm_power_off = NULL;
return 0;
}
static const struct of_device_id of_gpio_poweroff_match[] = {
{ .compatible = "gpio-poweroff", },
{},
};
static struct platform_driver gpio_poweroff_driver = {
.probe = gpio_poweroff_probe,
.remove = gpio_poweroff_remove,
.driver = {
.name = "poweroff-gpio",
.of_match_table = of_gpio_poweroff_match,
},
};
module_platform_driver(gpio_poweroff_driver);
MODULE_AUTHOR("Jamie Lentin ");
MODULE_DESCRIPTION("GPIO poweroff driver");
MODULE_LICENSE("GPL v2");
MODULE_ALIAS("platform:poweroff-gpio");
Device Drivers --->
-*- Power supply class support --->
[*] Board level reset or power off --->
[*] GPIO power-off driver
关机的信号和按键有关系,我是这样实现的:
gpio-keys {
compatible = "gpio-keys";
pinctrl-names = "default";
pinctrl-0 = <&pinctrl_gpio_keys>;
power-key {
label = "power-key";
gpios = <&gpio4 5 GPIO_ACTIVE_LOW>;
gpio-key,wakeup;
linux,code = ;
};
}
我只修改了设备树,使用的是现成的驱动。
linux,code = ;