^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 1) // SPDX-License-Identifier: GPL-2.0-only
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 2) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 3) * Toggles a GPIO pin to power down a device
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 4) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 5) * Jamie Lentin <jm@lentin.co.uk>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 6) * Andrew Lunn <andrew@lunn.ch>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 7) *
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 8) * Copyright (C) 2012 Jamie Lentin
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 9) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 10) #include <linux/kernel.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 11) #include <linux/init.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 12) #include <linux/delay.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 13) #include <linux/platform_device.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 14) #include <linux/gpio/consumer.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 15) #include <linux/of_platform.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 16) #include <linux/module.h>
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 17)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 18) #define DEFAULT_TIMEOUT_MS 3000
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 19) /*
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 20) * Hold configuration here, cannot be more than one instance of the driver
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 21) * since pm_power_off itself is global.
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 22) */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 23) static struct gpio_desc *reset_gpio;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 24) static u32 timeout = DEFAULT_TIMEOUT_MS;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 25) static u32 active_delay = 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 26) static u32 inactive_delay = 100;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 27)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 28) static void gpio_poweroff_do_poweroff(void)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 29) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 30) BUG_ON(!reset_gpio);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 31)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 32) /* drive it active, also inactive->active edge */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 33) gpiod_direction_output(reset_gpio, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 34) mdelay(active_delay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 35)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 36) /* drive inactive, also active->inactive edge */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 37) gpiod_set_value_cansleep(reset_gpio, 0);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 38) mdelay(inactive_delay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 39)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 40) /* drive it active, also inactive->active edge */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 41) gpiod_set_value_cansleep(reset_gpio, 1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 42)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 43) /* give it some time */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 44) mdelay(timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 45)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 46) WARN_ON(1);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 47) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 48)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 49) static int gpio_poweroff_probe(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 50) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 51) bool input = false;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 52) enum gpiod_flags flags;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 53)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 54) /* If a pm_power_off function has already been added, leave it alone */
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 55) if (pm_power_off != NULL) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 56) dev_err(&pdev->dev,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 57) "%s: pm_power_off function already registered\n",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 58) __func__);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 59) return -EBUSY;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 60) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 61)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 62) input = device_property_read_bool(&pdev->dev, "input");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 63) if (input)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 64) flags = GPIOD_IN;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 65) else
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 66) flags = GPIOD_OUT_LOW;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 67)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 68) device_property_read_u32(&pdev->dev, "active-delay-ms", &active_delay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 69) device_property_read_u32(&pdev->dev, "inactive-delay-ms",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 70) &inactive_delay);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 71) device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 72)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 73) reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 74) if (IS_ERR(reset_gpio))
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 75) return PTR_ERR(reset_gpio);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 76)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 77) pm_power_off = &gpio_poweroff_do_poweroff;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 78) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 79) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 80)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 81) static int gpio_poweroff_remove(struct platform_device *pdev)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 82) {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 83) if (pm_power_off == &gpio_poweroff_do_poweroff)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 84) pm_power_off = NULL;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 85)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 86) return 0;
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 87) }
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 88)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 89) static const struct of_device_id of_gpio_poweroff_match[] = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 90) { .compatible = "gpio-poweroff", },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 91) {},
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 92) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 93) MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 94)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 95) static struct platform_driver gpio_poweroff_driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 96) .probe = gpio_poweroff_probe,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 97) .remove = gpio_poweroff_remove,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 98) .driver = {
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 99) .name = "poweroff-gpio",
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 100) .of_match_table = of_gpio_poweroff_match,
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 101) },
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 102) };
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 103)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 104) module_platform_driver(gpio_poweroff_driver);
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 105)
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 106) MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 107) MODULE_DESCRIPTION("GPIO poweroff driver");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 108) MODULE_LICENSE("GPL v2");
^8f3ce5b39 (kx 2023-10-28 12:00:06 +0300 109) MODULE_ALIAS("platform:poweroff-gpio");