Commit | Line | Data |
---|---|---|
68d8bf04 FB |
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> | |
b07682b6 | 30 | #include <linux/i2c/twl.h> |
68d8bf04 FB |
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 |