diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h index eec0c0bda7668f13c2ead9b05fd104463dc1e21b..4e3a1dca8959f35aa1045ae484ea53b455725961 100644 --- a/arch/arm/include/asm/mach/arch.h +++ b/arch/arm/include/asm/mach/arch.h @@ -60,6 +60,7 @@ struct machine_desc { void (*handle_irq)(struct pt_regs *); #endif void (*restart)(enum reboot_mode, const char *); + void (*pwroff)(void); }; /* diff --git a/arch/arm/include/asm/system_misc.h b/arch/arm/include/asm/system_misc.h index 66f6a3ae68d273ba0f08e827b15f27c093cf8867..d1e450e00fd296bf4ac37cdb016b85b5ae1641f1 100644 --- a/arch/arm/include/asm/system_misc.h +++ b/arch/arm/include/asm/system_misc.h @@ -15,6 +15,8 @@ extern void cpu_init(void); void soft_restart(unsigned long); extern void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); extern void (*arm_pm_idle)(void); +extern void (*arm_pm_poweroff)(void); + #ifdef CONFIG_HARDEN_BRANCH_PREDICTOR typedef void (*harden_branch_predictor_fn_t)(void); diff --git a/arch/arm/kernel/reboot.c b/arch/arm/kernel/reboot.c index 0ce388f15422646023f64f7e888db942f1ddb5b0..241b9c4d6cabd444c43ef6be3941f51d089b2930 100644 --- a/arch/arm/kernel/reboot.c +++ b/arch/arm/kernel/reboot.c @@ -19,6 +19,7 @@ typedef void (*phys_reset_t)(unsigned long, bool); * Function pointers to optional machine specific functions */ void (*arm_pm_restart)(enum reboot_mode reboot_mode, const char *cmd); +void (*arm_pm_poweroff)(void); void (*pm_power_off)(void); EXPORT_SYMBOL(pm_power_off); @@ -104,6 +105,10 @@ void machine_halt(void) { local_irq_disable(); smp_send_stop(); + + if ( arm_pm_poweroff ) + arm_pm_poweroff (); + while (1); } diff --git a/arch/arm/kernel/setup.c b/arch/arm/kernel/setup.c index b06602cea99c786bc478bdd6b23e835efa9885b2..c031ce3f9ff89d5e88fc2c72bc5e4ee3369697e8 100644 --- a/arch/arm/kernel/setup.c +++ b/arch/arm/kernel/setup.c @@ -1152,6 +1152,9 @@ void __init setup_arch(char **cmdline_p) if (mdesc->restart) arm_pm_restart = mdesc->restart; + if (mdesc->pwroff) + arm_pm_poweroff = mdesc->pwroff; + unflatten_device_tree(); arm_dt_init_cpu_maps(); diff --git a/arch/arm/mach-imx/mach-imx6q.c b/arch/arm/mach-imx/mach-imx6q.c index 5bdb65dcd67fb221a4a39584994fb444b195c73a..0506ce51f550a7c35c94ce13bd8c16da26c9a6a7 100644 --- a/arch/arm/mach-imx/mach-imx6q.c +++ b/arch/arm/mach-imx/mach-imx6q.c @@ -7,12 +7,14 @@ #include <linux/clk.h> #include <linux/irqchip.h> #include <linux/of_platform.h> +#include <linux/gpio.h> #include <linux/pci.h> #include <linux/phy.h> #include <linux/regmap.h> #include <linux/micrel_phy.h> #include <linux/mfd/syscon.h> #include <linux/mfd/syscon/imx6q-iomuxc-gpr.h> +#include <linux/of_gpio.h> #include <asm/mach/arch.h> #include <asm/mach/map.h> @@ -20,6 +22,10 @@ #include "cpuidle.h" #include "hardware.h" +static int power_gpio = -1; +static int power_gpio_pol = 0; +static int only_for_poweroff = 0; + /* For imx6q sabrelite board: set KSZ9021RN RGMII pad skew */ static int ksz9021rn_phy_fixup(struct phy_device *phydev) { @@ -324,8 +330,45 @@ static void __init imx6q_init_machine(void) imx6q_axi_init(); } +static void imx6q_poweroff (void) { + int ret; + + if ( power_gpio_pol ) + ret = gpio_request_one (power_gpio, GPIOF_OUT_INIT_HIGH, "power_gpio"); + else + ret = gpio_request_one (power_gpio, GPIOF_OUT_INIT_LOW, "power_gpio"); + + if ( ret < 0) + printk (KERN_ERR "(%d) Unable to get kill power GPIO (%d)\n", ret, power_gpio); +}; + + +void imx6q_restart (enum reboot_mode reboot_mode, const char *cmd) { + int ret = 0; + int ectrl_signed = 0; + + if ( !only_for_poweroff ) { + if ( power_gpio_pol ) + ret = gpio_request_one (power_gpio, GPIOF_OUT_INIT_HIGH, "power_gpio"); + else + ret = gpio_request_one (power_gpio, GPIOF_OUT_INIT_LOW, "power_gpio"); + + if ( ret < 0) + printk (KERN_ERR "Unable to get kill power GPIO\n"); + else { + ectrl_signed = 1; + } + } + + if ( !ectrl_signed ) + mxc_restart (reboot_mode, cmd); +} + + + static void __init imx6q_init_late(void) { + struct device_node *np; /* * WAIT mode is broken on imx6 Dual/Quad revision 1.0 and 1.1 so * there is no point to run cpuidle on them. @@ -338,6 +381,19 @@ static void __init imx6q_init_late(void) if (IS_ENABLED(CONFIG_ARM_IMX6Q_CPUFREQ)) platform_device_register_simple("imx6q-cpufreq", -1, NULL, 0); + np = of_find_node_by_path ("/power_signal"); + if ( np ) { + power_gpio = of_get_named_gpio (np, "power-gpio", 0); + if ( gpio_is_valid (power_gpio) ) { + pm_power_off = imx6q_poweroff; + printk (KERN_WARNING "kill power GPIO %d ok\n", power_gpio); + } + else + printk (KERN_WARNING "Unable to get kill power GPIO\n"); + power_gpio_pol = of_property_read_bool(np, "set_high"); + only_for_poweroff = of_property_read_bool(np, "only_for_poweroff"); + np = NULL; + } } static void __init imx6q_map_io(void) @@ -374,4 +430,6 @@ DT_MACHINE_START(IMX6Q, "Freescale i.MX6 Quad/DualLite (Device Tree)") .init_machine = imx6q_init_machine, .init_late = imx6q_init_late, .dt_compat = imx6q_dt_compat, + .restart = imx6q_restart, + .pwroff = imx6q_poweroff, MACHINE_END