This is a straight port from Linux v5.11. Signed-off-by: Ahmad Fatoum <ahmad@xxxxxx> --- drivers/power/reset/Kconfig | 8 +++ drivers/power/reset/Makefile | 1 + drivers/power/reset/gpio-poweroff.c | 93 +++++++++++++++++++++++++++++ 3 files changed, 102 insertions(+) create mode 100644 drivers/power/reset/gpio-poweroff.c diff --git a/drivers/power/reset/Kconfig b/drivers/power/reset/Kconfig index e60037a6e637..cadcc0b13f2f 100644 --- a/drivers/power/reset/Kconfig +++ b/drivers/power/reset/Kconfig @@ -28,3 +28,11 @@ config POWER_RESET_SYSCON_POWEROFF select MFD_SYSCON help Poweroff support for generic SYSCON mapped register poweroff. + +config POWER_RESET_GPIO + bool "GPIO power-off driver" + depends on OF_GPIO + help + This driver supports turning off your board via a GPIO line. + If your board needs a GPIO high/low to power down, say Y and + create a binding in your devicetree. diff --git a/drivers/power/reset/Makefile b/drivers/power/reset/Makefile index a490dce87333..438c85970028 100644 --- a/drivers/power/reset/Makefile +++ b/drivers/power/reset/Makefile @@ -3,3 +3,4 @@ obj-$(CONFIG_REBOOT_MODE) += reboot-mode.o obj-$(CONFIG_SYSCON_REBOOT_MODE) += syscon-reboot-mode.o obj-$(CONFIG_POWER_RESET_SYSCON) += syscon-reboot.o obj-$(CONFIG_POWER_RESET_SYSCON_POWEROFF) += syscon-poweroff.o +obj-$(CONFIG_POWER_RESET_GPIO) += gpio-poweroff.o diff --git a/drivers/power/reset/gpio-poweroff.c b/drivers/power/reset/gpio-poweroff.c new file mode 100644 index 000000000000..45b0d274e729 --- /dev/null +++ b/drivers/power/reset/gpio-poweroff.c @@ -0,0 +1,93 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Toggles a GPIO pin to power down a device + * + * Jamie Lentin <jm@xxxxxxxxxxxx> + * Andrew Lunn <andrew@xxxxxxx> + * + * Copyright (C) 2012 Jamie Lentin + */ +#include <common.h> +#include <driver.h> +#include <poweroff.h> +#include <gpiod.h> + +#define DEFAULT_TIMEOUT_MS 3000 +/* + * Hold configuration here, cannot be more than one instance of the driver + * since pm_power_off itself is global. + */ +static int reset_gpio; +static u32 timeout = DEFAULT_TIMEOUT_MS; +static u32 active_delay = 100; +static u32 inactive_delay = 100; + +static void gpio_poweroff_do_poweroff(struct poweroff_handler *handler) +{ + /* drive it active, also inactive->active edge */ + gpio_direction_active(reset_gpio, true); + mdelay(active_delay); + + /* drive inactive, also active->inactive edge */ + gpio_set_active(reset_gpio, false); + mdelay(inactive_delay); + + /* drive it active, also inactive->active edge */ + gpio_set_active(reset_gpio, true); + + /* give it some time */ + mdelay(timeout); + + WARN_ON(1); +} + +static struct poweroff_handler handler; + +static int gpio_poweroff_probe(struct device_d *dev) +{ + struct device_node *np = dev->device_node; + bool input = false; + enum gpiod_flags flags; + + if (handler.poweroff != NULL) { + dev_err(dev, "%s: pm_power_off function already registered\n", __func__); + return -EBUSY; + } + + input = of_property_read_bool(np, "input"); + if (input) + flags = GPIOD_IN; + else + flags = GPIOD_OUT_LOW; + + of_property_read_u32(np, "active-delay-ms", &active_delay); + of_property_read_u32(np, "inactive-delay-ms", &inactive_delay); + of_property_read_u32(np, "timeout-ms", &timeout); + + reset_gpio = gpiod_get(dev, NULL, flags); + if (reset_gpio < 0) + return reset_gpio; + + handler.name = "gpio-poweroff"; + handler.poweroff = gpio_poweroff_do_poweroff; + handler.priority = 129; + + return poweroff_handler_register(&handler); +} + +static const struct of_device_id of_gpio_poweroff_match[] = { + { .compatible = "gpio-poweroff", }, + {}, +}; + +static struct driver_d gpio_poweroff_driver = { + .name = "poweroff-gpio", + .of_compatible = of_gpio_poweroff_match, + .probe = gpio_poweroff_probe, +}; +device_platform_driver(gpio_poweroff_driver); + +MODULE_AUTHOR("Jamie Lentin <jm@xxxxxxxxxxxx>"); +MODULE_DESCRIPTION("GPIO poweroff driver"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:poweroff-gpio"); -- 2.30.0 _______________________________________________ barebox mailing list barebox@xxxxxxxxxxxxxxxxxxx http://lists.infradead.org/mailman/listinfo/barebox