twl4030-pwrbutton.c 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. /*
  2. * TWL4030 Power Button Input Driver
  3. *
  4. * Copyright (C) 2008-2009 Nokia Corporation
  5. *
  6. * Written by Peter De Schrijver <peter.de-schrijver@nokia.com>
  7. * Several fixes by Felipe Balbi <felipe.balbi@nokia.com>
  8. *
  9. * This file is subject to the terms and conditions of the GNU General
  10. * Public License. See the file "COPYING" in the main directory of this
  11. * archive for more details.
  12. *
  13. * This program is distributed in the hope that it will be useful,
  14. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  15. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  16. * GNU General Public License for more details.
  17. *
  18. * You should have received a copy of the GNU General Public License
  19. * along with this program; if not, write to the Free Software
  20. * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
  21. */
  22. #include <linux/bits.h>
  23. #include <linux/module.h>
  24. #include <linux/init.h>
  25. #include <linux/kernel.h>
  26. #include <linux/errno.h>
  27. #include <linux/input.h>
  28. #include <linux/interrupt.h>
  29. #include <linux/mod_devicetable.h>
  30. #include <linux/property.h>
  31. #include <linux/platform_device.h>
  32. #include <linux/mfd/twl.h>
  33. #define PWR_PWRON_IRQ BIT(0)
  34. struct twl_pwrbutton_chipdata {
  35. u8 status_reg;
  36. bool need_manual_irq;
  37. };
  38. static const struct twl_pwrbutton_chipdata twl4030_chipdata = {
  39. .status_reg = 0xf,
  40. .need_manual_irq = false,
  41. };
  42. static const struct twl_pwrbutton_chipdata twl6030_chipdata = {
  43. .status_reg = 0x2,
  44. .need_manual_irq = true,
  45. };
  46. static irqreturn_t powerbutton_irq(int irq, void *_pwr)
  47. {
  48. struct input_dev *pwr = _pwr;
  49. const struct twl_pwrbutton_chipdata *pdata = dev_get_drvdata(pwr->dev.parent);
  50. int err;
  51. u8 value;
  52. err = twl_i2c_read_u8(TWL_MODULE_PM_MASTER, &value, pdata->status_reg);
  53. if (!err) {
  54. pm_wakeup_event(pwr->dev.parent, 0);
  55. input_report_key(pwr, KEY_POWER, value & PWR_PWRON_IRQ);
  56. input_sync(pwr);
  57. } else {
  58. dev_err(pwr->dev.parent, "twl4030: i2c error %d while reading"
  59. " TWL4030 PM_MASTER STS_HW_CONDITIONS register\n", err);
  60. }
  61. return IRQ_HANDLED;
  62. }
  63. static int twl4030_pwrbutton_probe(struct platform_device *pdev)
  64. {
  65. const struct twl_pwrbutton_chipdata *pdata;
  66. struct input_dev *pwr;
  67. int irq = platform_get_irq(pdev, 0);
  68. int err;
  69. pdata = device_get_match_data(&pdev->dev);
  70. if (!pdata)
  71. return -EINVAL;
  72. platform_set_drvdata(pdev, (void *)pdata);
  73. pwr = devm_input_allocate_device(&pdev->dev);
  74. if (!pwr) {
  75. dev_err(&pdev->dev, "Can't allocate power button\n");
  76. return -ENOMEM;
  77. }
  78. input_set_capability(pwr, EV_KEY, KEY_POWER);
  79. pwr->name = "twl4030_pwrbutton";
  80. pwr->phys = "twl4030_pwrbutton/input0";
  81. pwr->dev.parent = &pdev->dev;
  82. err = devm_request_threaded_irq(&pdev->dev, irq, NULL, powerbutton_irq,
  83. IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING |
  84. IRQF_ONESHOT,
  85. "twl4030_pwrbutton", pwr);
  86. if (err < 0) {
  87. dev_err(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err);
  88. return err;
  89. }
  90. err = input_register_device(pwr);
  91. if (err) {
  92. dev_err(&pdev->dev, "Can't register power button: %d\n", err);
  93. return err;
  94. }
  95. if (pdata->need_manual_irq) {
  96. err = twl6030_interrupt_unmask(0x01, REG_INT_MSK_LINE_A);
  97. if (err)
  98. return err;
  99. err = twl6030_interrupt_unmask(0x01, REG_INT_MSK_STS_A);
  100. if (err)
  101. return err;
  102. }
  103. device_init_wakeup(&pdev->dev, true);
  104. return 0;
  105. }
  106. static void twl4030_pwrbutton_remove(struct platform_device *pdev)
  107. {
  108. const struct twl_pwrbutton_chipdata *pdata = platform_get_drvdata(pdev);
  109. if (pdata->need_manual_irq) {
  110. twl6030_interrupt_mask(0x01, REG_INT_MSK_LINE_A);
  111. twl6030_interrupt_mask(0x01, REG_INT_MSK_STS_A);
  112. }
  113. }
  114. static const struct of_device_id twl4030_pwrbutton_dt_match_table[] = {
  115. {
  116. .compatible = "ti,twl4030-pwrbutton",
  117. .data = &twl4030_chipdata,
  118. },
  119. {
  120. .compatible = "ti,twl6030-pwrbutton",
  121. .data = &twl6030_chipdata,
  122. },
  123. { }
  124. };
  125. MODULE_DEVICE_TABLE(of, twl4030_pwrbutton_dt_match_table);
  126. static struct platform_driver twl4030_pwrbutton_driver = {
  127. .probe = twl4030_pwrbutton_probe,
  128. .remove = twl4030_pwrbutton_remove,
  129. .driver = {
  130. .name = "twl4030_pwrbutton",
  131. .of_match_table = twl4030_pwrbutton_dt_match_table,
  132. },
  133. };
  134. module_platform_driver(twl4030_pwrbutton_driver);
  135. MODULE_ALIAS("platform:twl4030_pwrbutton");
  136. MODULE_DESCRIPTION("Triton2 Power Button");
  137. MODULE_LICENSE("GPL");
  138. MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>");
  139. MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>");