gpio-poweroff.c 2.9 KB

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