diff options
Diffstat (limited to 'drivers/input/misc/twl4030-pwrbutton.c')
-rw-r--r-- | drivers/input/misc/twl4030-pwrbutton.c | 145 |
1 files changed, 145 insertions, 0 deletions
diff --git a/drivers/input/misc/twl4030-pwrbutton.c b/drivers/input/misc/twl4030-pwrbutton.c new file mode 100644 index 000000000000..f5fc9974a111 --- /dev/null +++ b/drivers/input/misc/twl4030-pwrbutton.c | |||
@@ -0,0 +1,145 @@ | |||
1 | /** | ||
2 | * twl4030-pwrbutton.c - 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 | |||
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/platform_device.h> | ||
30 | #include <linux/i2c/twl4030.h> | ||
31 | |||
32 | #define PWR_PWRON_IRQ (1 << 0) | ||
33 | |||
34 | #define STS_HW_CONDITIONS 0xf | ||
35 | |||
36 | static irqreturn_t powerbutton_irq(int irq, void *_pwr) | ||
37 | { | ||
38 | struct input_dev *pwr = _pwr; | ||
39 | int err; | ||
40 | u8 value; | ||
41 | |||
42 | #ifdef CONFIG_LOCKDEP | ||
43 | /* WORKAROUND for lockdep forcing IRQF_DISABLED on us, which | ||
44 | * we don't want and can't tolerate since this is a threaded | ||
45 | * IRQ and can sleep due to the i2c reads it has to issue. | ||
46 | * Although it might be friendlier not to borrow this thread | ||
47 | * context... | ||
48 | */ | ||
49 | local_irq_enable(); | ||
50 | #endif | ||
51 | |||
52 | err = twl4030_i2c_read_u8(TWL4030_MODULE_PM_MASTER, &value, | ||
53 | STS_HW_CONDITIONS); | ||
54 | if (!err) { | ||
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 | |||
62 | return IRQ_HANDLED; | ||
63 | } | ||
64 | |||
65 | static int __devinit twl4030_pwrbutton_probe(struct platform_device *pdev) | ||
66 | { | ||
67 | struct input_dev *pwr; | ||
68 | int irq = platform_get_irq(pdev, 0); | ||
69 | int err; | ||
70 | |||
71 | pwr = input_allocate_device(); | ||
72 | if (!pwr) { | ||
73 | dev_dbg(&pdev->dev, "Can't allocate power button\n"); | ||
74 | return -ENOMEM; | ||
75 | } | ||
76 | |||
77 | pwr->evbit[0] = BIT_MASK(EV_KEY); | ||
78 | pwr->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); | ||
79 | pwr->name = "twl4030_pwrbutton"; | ||
80 | pwr->phys = "twl4030_pwrbutton/input0"; | ||
81 | pwr->dev.parent = &pdev->dev; | ||
82 | |||
83 | err = request_irq(irq, powerbutton_irq, | ||
84 | IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, | ||
85 | "twl4030_pwrbutton", pwr); | ||
86 | if (err < 0) { | ||
87 | dev_dbg(&pdev->dev, "Can't get IRQ for pwrbutton: %d\n", err); | ||
88 | goto free_input_dev; | ||
89 | } | ||
90 | |||
91 | err = input_register_device(pwr); | ||
92 | if (err) { | ||
93 | dev_dbg(&pdev->dev, "Can't register power button: %d\n", err); | ||
94 | goto free_irq; | ||
95 | } | ||
96 | |||
97 | platform_set_drvdata(pdev, pwr); | ||
98 | |||
99 | return 0; | ||
100 | |||
101 | free_irq: | ||
102 | free_irq(irq, NULL); | ||
103 | free_input_dev: | ||
104 | input_free_device(pwr); | ||
105 | return err; | ||
106 | } | ||
107 | |||
108 | static int __devexit twl4030_pwrbutton_remove(struct platform_device *pdev) | ||
109 | { | ||
110 | struct input_dev *pwr = platform_get_drvdata(pdev); | ||
111 | int irq = platform_get_irq(pdev, 0); | ||
112 | |||
113 | free_irq(irq, pwr); | ||
114 | input_unregister_device(pwr); | ||
115 | |||
116 | return 0; | ||
117 | } | ||
118 | |||
119 | struct platform_driver twl4030_pwrbutton_driver = { | ||
120 | .probe = twl4030_pwrbutton_probe, | ||
121 | .remove = __devexit_p(twl4030_pwrbutton_remove), | ||
122 | .driver = { | ||
123 | .name = "twl4030_pwrbutton", | ||
124 | .owner = THIS_MODULE, | ||
125 | }, | ||
126 | }; | ||
127 | |||
128 | static int __init twl4030_pwrbutton_init(void) | ||
129 | { | ||
130 | return platform_driver_register(&twl4030_pwrbutton_driver); | ||
131 | } | ||
132 | module_init(twl4030_pwrbutton_init); | ||
133 | |||
134 | static void __exit twl4030_pwrbutton_exit(void) | ||
135 | { | ||
136 | platform_driver_unregister(&twl4030_pwrbutton_driver); | ||
137 | } | ||
138 | module_exit(twl4030_pwrbutton_exit); | ||
139 | |||
140 | MODULE_ALIAS("platform:twl4030_pwrbutton"); | ||
141 | MODULE_DESCRIPTION("Triton2 Power Button"); | ||
142 | MODULE_LICENSE("GPL"); | ||
143 | MODULE_AUTHOR("Peter De Schrijver <peter.de-schrijver@nokia.com>"); | ||
144 | MODULE_AUTHOR("Felipe Balbi <felipe.balbi@nokia.com>"); | ||
145 | |||