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/* 23 * Hold configuration here, cannot be more than one instance of the driver 24 * since pm_power_off itself is global. 25 */ 26static struct gpio_desc *reset_gpio; 27 28static void gpio_poweroff_do_poweroff(void) 29{ 30 BUG_ON(!reset_gpio); 31 32 /* drive it active, also inactive->active edge */ 33 gpiod_direction_output(reset_gpio, 1); 34 mdelay(100); 35 /* drive inactive, also active->inactive edge */ 36 gpiod_set_value(reset_gpio, 0); 37 mdelay(100); 38 39 /* drive it active, also inactive->active edge */ 40 gpiod_set_value(reset_gpio, 1); 41 42 /* give it some time */ 43 mdelay(3000); 44 45 WARN_ON(1); 46} 47 48static int gpio_poweroff_probe(struct platform_device *pdev) 49{ 50 bool input = false; 51 52 /* If a pm_power_off function has already been added, leave it alone */ 53 if (pm_power_off != NULL) { 54 dev_err(&pdev->dev, 55 "%s: pm_power_off function already registered", 56 __func__); 57 return -EBUSY; 58 } 59 60 reset_gpio = devm_gpiod_get(&pdev->dev, NULL); 61 if (IS_ERR(reset_gpio)) 62 return PTR_ERR(reset_gpio); 63 64 input = of_property_read_bool(pdev->dev.of_node, "input"); 65 66 if (input) { 67 if (gpiod_direction_input(reset_gpio)) { 68 dev_err(&pdev->dev, 69 "Could not set direction of reset GPIO to input\n"); 70 return -ENODEV; 71 } 72 } else { 73 if (gpiod_direction_output(reset_gpio, 0)) { 74 dev_err(&pdev->dev, 75 "Could not set direction of reset GPIO\n"); 76 return -ENODEV; 77 } 78 } 79 80 pm_power_off = &gpio_poweroff_do_poweroff; 81 return 0; 82} 83 84static int gpio_poweroff_remove(struct platform_device *pdev) 85{ 86 if (pm_power_off == &gpio_poweroff_do_poweroff) 87 pm_power_off = NULL; 88 89 return 0; 90} 91 92static const struct of_device_id of_gpio_poweroff_match[] = { 93 { .compatible = "gpio-poweroff", }, 94 {}, 95}; 96 97static struct platform_driver gpio_poweroff_driver = { 98 .probe = gpio_poweroff_probe, 99 .remove = gpio_poweroff_remove, 100 .driver = { 101 .name = "poweroff-gpio", 102 .owner = THIS_MODULE, 103 .of_match_table = of_gpio_poweroff_match, 104 }, 105}; 106 107module_platform_driver(gpio_poweroff_driver); 108 109MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>"); 110MODULE_DESCRIPTION("GPIO poweroff driver"); 111MODULE_LICENSE("GPL v2"); 112MODULE_ALIAS("platform:poweroff-gpio"); 113