Branch data Line data Source code
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/gpio/consumer.h> 15 : : #include <linux/of_platform.h> 16 : : #include <linux/module.h> 17 : : 18 : : #define DEFAULT_TIMEOUT_MS 3000 19 : : /* 20 : : * Hold configuration here, cannot be more than one instance of the driver 21 : : * since pm_power_off itself is global. 22 : : */ 23 : : static struct gpio_desc *reset_gpio; 24 : : static u32 timeout = DEFAULT_TIMEOUT_MS; 25 : : static u32 active_delay = 100; 26 : : static u32 inactive_delay = 100; 27 : : 28 : 0 : static void gpio_poweroff_do_poweroff(void) 29 : : { 30 : 0 : BUG_ON(!reset_gpio); 31 : : 32 : : /* drive it active, also inactive->active edge */ 33 : 0 : gpiod_direction_output(reset_gpio, 1); 34 : 0 : mdelay(active_delay); 35 : : 36 : : /* drive inactive, also active->inactive edge */ 37 : 0 : gpiod_set_value_cansleep(reset_gpio, 0); 38 : 0 : mdelay(inactive_delay); 39 : : 40 : : /* drive it active, also inactive->active edge */ 41 : 0 : gpiod_set_value_cansleep(reset_gpio, 1); 42 : : 43 : : /* give it some time */ 44 : 0 : mdelay(timeout); 45 : : 46 : 0 : WARN_ON(1); 47 : 0 : } 48 : : 49 : 0 : static int gpio_poweroff_probe(struct platform_device *pdev) 50 : : { 51 : : bool input = false; 52 : : enum gpiod_flags flags; 53 : : bool force = false; 54 : : bool export = false; 55 : : 56 : : /* If a pm_power_off function has already been added, leave it alone */ 57 : 0 : force = of_property_read_bool(pdev->dev.of_node, "force"); 58 : 0 : if (!force && (pm_power_off != NULL)) { 59 : 0 : dev_err(&pdev->dev, 60 : : "%s: pm_power_off function already registered", 61 : : __func__); 62 : 0 : return -EBUSY; 63 : : } 64 : : 65 : 0 : input = device_property_read_bool(&pdev->dev, "input"); 66 : 0 : if (input) 67 : : flags = GPIOD_IN; 68 : : else 69 : : flags = GPIOD_OUT_LOW; 70 : : 71 : : device_property_read_u32(&pdev->dev, "active-delay-ms", &active_delay); 72 : : device_property_read_u32(&pdev->dev, "inactive-delay-ms", 73 : : &inactive_delay); 74 : : device_property_read_u32(&pdev->dev, "timeout-ms", &timeout); 75 : : 76 : 0 : reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags); 77 : 0 : if (IS_ERR(reset_gpio)) 78 : 0 : return PTR_ERR(reset_gpio); 79 : : 80 : 0 : export = of_property_read_bool(pdev->dev.of_node, "export"); 81 : 0 : if (export) { 82 : 0 : gpiod_export(reset_gpio, false); 83 : 0 : gpiod_export_link(&pdev->dev, "poweroff-gpio", reset_gpio); 84 : : } 85 : : 86 : 0 : pm_power_off = &gpio_poweroff_do_poweroff; 87 : 0 : return 0; 88 : : } 89 : : 90 : 0 : static int gpio_poweroff_remove(struct platform_device *pdev) 91 : : { 92 : 0 : if (pm_power_off == &gpio_poweroff_do_poweroff) 93 : 0 : pm_power_off = NULL; 94 : : 95 : 0 : gpiod_unexport(reset_gpio); 96 : : 97 : 0 : return 0; 98 : : } 99 : : 100 : : static const struct of_device_id of_gpio_poweroff_match[] = { 101 : : { .compatible = "gpio-poweroff", }, 102 : : {}, 103 : : }; 104 : : 105 : : static struct platform_driver gpio_poweroff_driver = { 106 : : .probe = gpio_poweroff_probe, 107 : : .remove = gpio_poweroff_remove, 108 : : .driver = { 109 : : .name = "poweroff-gpio", 110 : : .of_match_table = of_gpio_poweroff_match, 111 : : }, 112 : : }; 113 : : 114 : 3 : module_platform_driver(gpio_poweroff_driver); 115 : : 116 : : MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>"); 117 : : MODULE_DESCRIPTION("GPIO poweroff driver"); 118 : : MODULE_LICENSE("GPL v2"); 119 : : MODULE_ALIAS("platform:poweroff-gpio");