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