diff options
Diffstat (limited to 'drivers/input/misc/wm831x-on.c')
-rw-r--r-- | drivers/input/misc/wm831x-on.c | 163 |
1 files changed, 163 insertions, 0 deletions
diff --git a/drivers/input/misc/wm831x-on.c b/drivers/input/misc/wm831x-on.c new file mode 100644 index 000000000000..ba4f5dd7c60e --- /dev/null +++ b/drivers/input/misc/wm831x-on.c | |||
@@ -0,0 +1,163 @@ | |||
1 | /** | ||
2 | * wm831x-on.c - WM831X ON pin driver | ||
3 | * | ||
4 | * Copyright (C) 2009 Wolfson Microelectronics plc | ||
5 | * | ||
6 | * This file is subject to the terms and conditions of the GNU General | ||
7 | * Public License. See the file "COPYING" in the main directory of this | ||
8 | * archive for more details. | ||
9 | * | ||
10 | * This program is distributed in the hope that it will be useful, | ||
11 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
12 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
13 | * GNU General Public License for more details. | ||
14 | * | ||
15 | * You should have received a copy of the GNU General Public License | ||
16 | * along with this program; if not, write to the Free Software | ||
17 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | ||
18 | */ | ||
19 | |||
20 | #include <linux/module.h> | ||
21 | #include <linux/init.h> | ||
22 | #include <linux/kernel.h> | ||
23 | #include <linux/errno.h> | ||
24 | #include <linux/input.h> | ||
25 | #include <linux/interrupt.h> | ||
26 | #include <linux/platform_device.h> | ||
27 | #include <linux/workqueue.h> | ||
28 | #include <linux/mfd/wm831x/core.h> | ||
29 | |||
30 | struct wm831x_on { | ||
31 | struct input_dev *dev; | ||
32 | struct delayed_work work; | ||
33 | struct wm831x *wm831x; | ||
34 | }; | ||
35 | |||
36 | /* | ||
37 | * The chip gives us an interrupt when the ON pin is asserted but we | ||
38 | * then need to poll to see when the pin is deasserted. | ||
39 | */ | ||
40 | static void wm831x_poll_on(struct work_struct *work) | ||
41 | { | ||
42 | struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, | ||
43 | work.work); | ||
44 | struct wm831x *wm831x = wm831x_on->wm831x; | ||
45 | int poll, ret; | ||
46 | |||
47 | ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); | ||
48 | if (ret >= 0) { | ||
49 | poll = !(ret & WM831X_ON_PIN_STS); | ||
50 | |||
51 | input_report_key(wm831x_on->dev, KEY_POWER, poll); | ||
52 | input_sync(wm831x_on->dev); | ||
53 | } else { | ||
54 | dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); | ||
55 | poll = 1; | ||
56 | } | ||
57 | |||
58 | if (poll) | ||
59 | schedule_delayed_work(&wm831x_on->work, 100); | ||
60 | } | ||
61 | |||
62 | static irqreturn_t wm831x_on_irq(int irq, void *data) | ||
63 | { | ||
64 | struct wm831x_on *wm831x_on = data; | ||
65 | |||
66 | schedule_delayed_work(&wm831x_on->work, 0); | ||
67 | |||
68 | return IRQ_HANDLED; | ||
69 | } | ||
70 | |||
71 | static int __devinit wm831x_on_probe(struct platform_device *pdev) | ||
72 | { | ||
73 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | ||
74 | struct wm831x_on *wm831x_on; | ||
75 | int irq = platform_get_irq(pdev, 0); | ||
76 | int ret; | ||
77 | |||
78 | wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); | ||
79 | if (!wm831x_on) { | ||
80 | dev_err(&pdev->dev, "Can't allocate data\n"); | ||
81 | return -ENOMEM; | ||
82 | } | ||
83 | |||
84 | wm831x_on->wm831x = wm831x; | ||
85 | INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); | ||
86 | |||
87 | wm831x_on->dev = input_allocate_device(); | ||
88 | if (!wm831x_on->dev) { | ||
89 | dev_err(&pdev->dev, "Can't allocate input dev\n"); | ||
90 | ret = -ENOMEM; | ||
91 | goto err; | ||
92 | } | ||
93 | |||
94 | wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); | ||
95 | wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); | ||
96 | wm831x_on->dev->name = "wm831x_on"; | ||
97 | wm831x_on->dev->phys = "wm831x_on/input0"; | ||
98 | wm831x_on->dev->dev.parent = &pdev->dev; | ||
99 | |||
100 | ret = wm831x_request_irq(wm831x, irq, wm831x_on_irq, | ||
101 | IRQF_TRIGGER_RISING, "wm831x_on", wm831x_on); | ||
102 | if (ret < 0) { | ||
103 | dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); | ||
104 | goto err_input_dev; | ||
105 | } | ||
106 | ret = input_register_device(wm831x_on->dev); | ||
107 | if (ret) { | ||
108 | dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); | ||
109 | goto err_irq; | ||
110 | } | ||
111 | |||
112 | platform_set_drvdata(pdev, wm831x_on); | ||
113 | |||
114 | return 0; | ||
115 | |||
116 | err_irq: | ||
117 | wm831x_free_irq(wm831x, irq, NULL); | ||
118 | err_input_dev: | ||
119 | input_free_device(wm831x_on->dev); | ||
120 | err: | ||
121 | kfree(wm831x_on); | ||
122 | return ret; | ||
123 | } | ||
124 | |||
125 | static int __devexit wm831x_on_remove(struct platform_device *pdev) | ||
126 | { | ||
127 | struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); | ||
128 | int irq = platform_get_irq(pdev, 0); | ||
129 | |||
130 | wm831x_free_irq(wm831x_on->wm831x, irq, wm831x_on); | ||
131 | cancel_delayed_work_sync(&wm831x_on->work); | ||
132 | input_unregister_device(wm831x_on->dev); | ||
133 | kfree(wm831x_on); | ||
134 | |||
135 | return 0; | ||
136 | } | ||
137 | |||
138 | static struct platform_driver wm831x_on_driver = { | ||
139 | .probe = wm831x_on_probe, | ||
140 | .remove = __devexit_p(wm831x_on_remove), | ||
141 | .driver = { | ||
142 | .name = "wm831x-on", | ||
143 | .owner = THIS_MODULE, | ||
144 | }, | ||
145 | }; | ||
146 | |||
147 | static int __init wm831x_on_init(void) | ||
148 | { | ||
149 | return platform_driver_register(&wm831x_on_driver); | ||
150 | } | ||
151 | module_init(wm831x_on_init); | ||
152 | |||
153 | static void __exit wm831x_on_exit(void) | ||
154 | { | ||
155 | platform_driver_unregister(&wm831x_on_driver); | ||
156 | } | ||
157 | module_exit(wm831x_on_exit); | ||
158 | |||
159 | MODULE_ALIAS("platform:wm831x-on"); | ||
160 | MODULE_DESCRIPTION("WM831x ON pin"); | ||
161 | MODULE_LICENSE("GPL"); | ||
162 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | ||
163 | |||