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