Back to home page

OSCL-LXR

 
 

    


0001 // SPDX-License-Identifier: GPL-2.0-only
0002 /*
0003  * Toggles a GPIO pin to power down a device
0004  *
0005  * Jamie Lentin <jm@lentin.co.uk>
0006  * Andrew Lunn <andrew@lunn.ch>
0007  *
0008  * Copyright (C) 2012 Jamie Lentin
0009  */
0010 #include <linux/kernel.h>
0011 #include <linux/init.h>
0012 #include <linux/delay.h>
0013 #include <linux/platform_device.h>
0014 #include <linux/gpio/consumer.h>
0015 #include <linux/of_platform.h>
0016 #include <linux/module.h>
0017 
0018 #define DEFAULT_TIMEOUT_MS 3000
0019 /*
0020  * Hold configuration here, cannot be more than one instance of the driver
0021  * since pm_power_off itself is global.
0022  */
0023 static struct gpio_desc *reset_gpio;
0024 static u32 timeout = DEFAULT_TIMEOUT_MS;
0025 static u32 active_delay = 100;
0026 static u32 inactive_delay = 100;
0027 
0028 static void gpio_poweroff_do_poweroff(void)
0029 {
0030     BUG_ON(!reset_gpio);
0031 
0032     /* drive it active, also inactive->active edge */
0033     gpiod_direction_output(reset_gpio, 1);
0034     mdelay(active_delay);
0035 
0036     /* drive inactive, also active->inactive edge */
0037     gpiod_set_value_cansleep(reset_gpio, 0);
0038     mdelay(inactive_delay);
0039 
0040     /* drive it active, also inactive->active edge */
0041     gpiod_set_value_cansleep(reset_gpio, 1);
0042 
0043     /* give it some time */
0044     mdelay(timeout);
0045 
0046     WARN_ON(1);
0047 }
0048 
0049 static int gpio_poweroff_probe(struct platform_device *pdev)
0050 {
0051     bool input = false;
0052     enum gpiod_flags flags;
0053 
0054     /* If a pm_power_off function has already been added, leave it alone */
0055     if (pm_power_off != NULL) {
0056         dev_err(&pdev->dev,
0057             "%s: pm_power_off function already registered\n",
0058                __func__);
0059         return -EBUSY;
0060     }
0061 
0062     input = device_property_read_bool(&pdev->dev, "input");
0063     if (input)
0064         flags = GPIOD_IN;
0065     else
0066         flags = GPIOD_OUT_LOW;
0067 
0068     device_property_read_u32(&pdev->dev, "active-delay-ms", &active_delay);
0069     device_property_read_u32(&pdev->dev, "inactive-delay-ms",
0070                  &inactive_delay);
0071     device_property_read_u32(&pdev->dev, "timeout-ms", &timeout);
0072 
0073     reset_gpio = devm_gpiod_get(&pdev->dev, NULL, flags);
0074     if (IS_ERR(reset_gpio))
0075         return PTR_ERR(reset_gpio);
0076 
0077     pm_power_off = &gpio_poweroff_do_poweroff;
0078     return 0;
0079 }
0080 
0081 static int gpio_poweroff_remove(struct platform_device *pdev)
0082 {
0083     if (pm_power_off == &gpio_poweroff_do_poweroff)
0084         pm_power_off = NULL;
0085 
0086     return 0;
0087 }
0088 
0089 static const struct of_device_id of_gpio_poweroff_match[] = {
0090     { .compatible = "gpio-poweroff", },
0091     {},
0092 };
0093 MODULE_DEVICE_TABLE(of, of_gpio_poweroff_match);
0094 
0095 static struct platform_driver gpio_poweroff_driver = {
0096     .probe = gpio_poweroff_probe,
0097     .remove = gpio_poweroff_remove,
0098     .driver = {
0099         .name = "poweroff-gpio",
0100         .of_match_table = of_gpio_poweroff_match,
0101     },
0102 };
0103 
0104 module_platform_driver(gpio_poweroff_driver);
0105 
0106 MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");
0107 MODULE_DESCRIPTION("GPIO poweroff driver");
0108 MODULE_LICENSE("GPL v2");
0109 MODULE_ALIAS("platform:poweroff-gpio");