From 7de1d5d9fc5ca8f9296882f6a240569cd2aaf464 Mon Sep 17 00:00:00 2001 From: yuri <yuri.mazzuli@seco.com> Date: Thu, 20 Apr 2023 16:01:48 +0200 Subject: [PATCH] [IMX6][PWR] add poweroff and reboot system with kill power GPIO this feature allow the cpu to turn off the power of the board on poweroff using a GPIO; the kill power GPIO have to be specified in the device tree with a power_signal node: power_signal: power_signal { power-gpio = <&gpio2 4 0>; }; modified: arch/arm/include/asm/mach/arch.h modified: arch/arm/include/asm/system_misc.h modified: arch/arm/kernel/reboot.c modified: arch/arm/kernel/setup.c modified: arch/arm/mach-imx/mach-imx6q.c --- arch/arm/include/asm/mach/arch.h | 1 + arch/arm/include/asm/system_misc.h | 2 ++ arch/arm/kernel/reboot.c | 5 +++ arch/arm/kernel/setup.c | 3 ++ arch/arm/mach-imx/mach-imx6q.c | 58 ++++++++++++++++++++++++++++++ 5 files changed, 69 insertions(+) diff --git a/arch/arm/include/asm/mach/arch.h b/arch/arm/include/asm/mach/arch.h index eec0c0bda7668f..4e3a1dca8959f3 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 66f6a3ae68d273..d1e450e00fd296 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 0ce388f1542264..241b9c4d6cabd4 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 b06602cea99c78..c031ce3f9ff89d 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 5bdb65dcd67fb2..0506ce51f550a7 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 -- GitLab