Commit | Line | Data |
---|---|---|
0c73b992 MB |
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> | |
5a0e3ad6 | 22 | #include <linux/slab.h> |
0c73b992 MB |
23 | #include <linux/kernel.h> |
24 | #include <linux/errno.h> | |
25 | #include <linux/input.h> | |
26 | #include <linux/interrupt.h> | |
27 | #include <linux/platform_device.h> | |
28 | #include <linux/workqueue.h> | |
29 | #include <linux/mfd/wm831x/core.h> | |
30 | ||
31 | struct wm831x_on { | |
32 | struct input_dev *dev; | |
33 | struct delayed_work work; | |
34 | struct wm831x *wm831x; | |
35 | }; | |
36 | ||
37 | /* | |
38 | * The chip gives us an interrupt when the ON pin is asserted but we | |
39 | * then need to poll to see when the pin is deasserted. | |
40 | */ | |
41 | static void wm831x_poll_on(struct work_struct *work) | |
42 | { | |
43 | struct wm831x_on *wm831x_on = container_of(work, struct wm831x_on, | |
44 | work.work); | |
45 | struct wm831x *wm831x = wm831x_on->wm831x; | |
46 | int poll, ret; | |
47 | ||
48 | ret = wm831x_reg_read(wm831x, WM831X_ON_PIN_CONTROL); | |
49 | if (ret >= 0) { | |
50 | poll = !(ret & WM831X_ON_PIN_STS); | |
51 | ||
52 | input_report_key(wm831x_on->dev, KEY_POWER, poll); | |
53 | input_sync(wm831x_on->dev); | |
54 | } else { | |
55 | dev_err(wm831x->dev, "Failed to read ON status: %d\n", ret); | |
56 | poll = 1; | |
57 | } | |
58 | ||
59 | if (poll) | |
60 | schedule_delayed_work(&wm831x_on->work, 100); | |
61 | } | |
62 | ||
63 | static irqreturn_t wm831x_on_irq(int irq, void *data) | |
64 | { | |
65 | struct wm831x_on *wm831x_on = data; | |
66 | ||
67 | schedule_delayed_work(&wm831x_on->work, 0); | |
68 | ||
69 | return IRQ_HANDLED; | |
70 | } | |
71 | ||
72 | static int __devinit wm831x_on_probe(struct platform_device *pdev) | |
73 | { | |
74 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | |
75 | struct wm831x_on *wm831x_on; | |
76 | int irq = platform_get_irq(pdev, 0); | |
77 | int ret; | |
78 | ||
79 | wm831x_on = kzalloc(sizeof(struct wm831x_on), GFP_KERNEL); | |
80 | if (!wm831x_on) { | |
81 | dev_err(&pdev->dev, "Can't allocate data\n"); | |
82 | return -ENOMEM; | |
83 | } | |
84 | ||
85 | wm831x_on->wm831x = wm831x; | |
86 | INIT_DELAYED_WORK(&wm831x_on->work, wm831x_poll_on); | |
87 | ||
88 | wm831x_on->dev = input_allocate_device(); | |
89 | if (!wm831x_on->dev) { | |
90 | dev_err(&pdev->dev, "Can't allocate input dev\n"); | |
91 | ret = -ENOMEM; | |
92 | goto err; | |
93 | } | |
94 | ||
95 | wm831x_on->dev->evbit[0] = BIT_MASK(EV_KEY); | |
96 | wm831x_on->dev->keybit[BIT_WORD(KEY_POWER)] = BIT_MASK(KEY_POWER); | |
97 | wm831x_on->dev->name = "wm831x_on"; | |
98 | wm831x_on->dev->phys = "wm831x_on/input0"; | |
99 | wm831x_on->dev->dev.parent = &pdev->dev; | |
100 | ||
afadb8e0 MB |
101 | ret = request_threaded_irq(irq, NULL, wm831x_on_irq, |
102 | IRQF_TRIGGER_RISING, "wm831x_on", | |
103 | wm831x_on); | |
0c73b992 MB |
104 | if (ret < 0) { |
105 | dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); | |
106 | goto err_input_dev; | |
107 | } | |
108 | ret = input_register_device(wm831x_on->dev); | |
109 | if (ret) { | |
110 | dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); | |
111 | goto err_irq; | |
112 | } | |
113 | ||
114 | platform_set_drvdata(pdev, wm831x_on); | |
115 | ||
116 | return 0; | |
117 | ||
118 | err_irq: | |
afadb8e0 | 119 | free_irq(irq, wm831x_on); |
0c73b992 MB |
120 | err_input_dev: |
121 | input_free_device(wm831x_on->dev); | |
122 | err: | |
123 | kfree(wm831x_on); | |
124 | return ret; | |
125 | } | |
126 | ||
127 | static int __devexit wm831x_on_remove(struct platform_device *pdev) | |
128 | { | |
129 | struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); | |
130 | int irq = platform_get_irq(pdev, 0); | |
131 | ||
afadb8e0 | 132 | free_irq(irq, wm831x_on); |
0c73b992 MB |
133 | cancel_delayed_work_sync(&wm831x_on->work); |
134 | input_unregister_device(wm831x_on->dev); | |
135 | kfree(wm831x_on); | |
136 | ||
137 | return 0; | |
138 | } | |
139 | ||
140 | static struct platform_driver wm831x_on_driver = { | |
141 | .probe = wm831x_on_probe, | |
142 | .remove = __devexit_p(wm831x_on_remove), | |
143 | .driver = { | |
144 | .name = "wm831x-on", | |
145 | .owner = THIS_MODULE, | |
146 | }, | |
147 | }; | |
148 | ||
149 | static int __init wm831x_on_init(void) | |
150 | { | |
151 | return platform_driver_register(&wm831x_on_driver); | |
152 | } | |
153 | module_init(wm831x_on_init); | |
154 | ||
155 | static void __exit wm831x_on_exit(void) | |
156 | { | |
157 | platform_driver_unregister(&wm831x_on_driver); | |
158 | } | |
159 | module_exit(wm831x_on_exit); | |
160 | ||
161 | MODULE_ALIAS("platform:wm831x-on"); | |
162 | MODULE_DESCRIPTION("WM831x ON pin"); | |
163 | MODULE_LICENSE("GPL"); | |
164 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | |
165 |