gpio-poweroff.c 3.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129
  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_num == -1);
  31. /* drive it active */
  32. gpio_direction_output(gpio_num, !gpio_active_low);
  33. mdelay(100);
  34. /* rising edge or drive inactive */
  35. gpio_set_value(gpio_num, gpio_active_low);
  36. mdelay(100);
  37. /* falling 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_num < 0) {
  56. pr_err("%s: Could not get GPIO configuration: %d",
  57. __func__, gpio_num);
  58. return -ENODEV;
  59. }
  60. gpio_active_low = flags & OF_GPIO_ACTIVE_LOW;
  61. if (of_get_property(pdev->dev.of_node, "input", NULL))
  62. input = true;
  63. ret = gpio_request(gpio_num, "poweroff-gpio");
  64. if (ret) {
  65. pr_err("%s: Could not get GPIO %d", __func__, gpio_num);
  66. return ret;
  67. }
  68. if (input) {
  69. if (gpio_direction_input(gpio_num)) {
  70. pr_err("Could not set direction of GPIO %d to input",
  71. gpio_num);
  72. goto err;
  73. }
  74. } else {
  75. if (gpio_direction_output(gpio_num, gpio_active_low)) {
  76. pr_err("Could not set direction of GPIO %d", gpio_num);
  77. goto err;
  78. }
  79. }
  80. pm_power_off = &gpio_poweroff_do_poweroff;
  81. return 0;
  82. err:
  83. gpio_free(gpio_num);
  84. return -ENODEV;
  85. }
  86. static int gpio_poweroff_remove(struct platform_device *pdev)
  87. {
  88. if (gpio_num != -1)
  89. gpio_free(gpio_num);
  90. if (pm_power_off == &gpio_poweroff_do_poweroff)
  91. pm_power_off = NULL;
  92. return 0;
  93. }
  94. static const struct of_device_id of_gpio_poweroff_match[] = {
  95. { .compatible = "gpio-poweroff", },
  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. .owner = THIS_MODULE,
  104. .of_match_table = of_gpio_poweroff_match,
  105. },
  106. };
  107. module_platform_driver(gpio_poweroff_driver);
  108. MODULE_AUTHOR("Jamie Lentin <jm@lentin.co.uk>");
  109. MODULE_DESCRIPTION("GPIO poweroff driver");
  110. MODULE_LICENSE("GPL");
  111. MODULE_ALIAS("platform:poweroff-gpio");