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> | |
5a0e3ad6 | 21 | #include <linux/slab.h> |
0c73b992 MB |
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 | ||
5298cc4c | 71 | static int wm831x_on_probe(struct platform_device *pdev) |
0c73b992 MB |
72 | { |
73 | struct wm831x *wm831x = dev_get_drvdata(pdev->dev.parent); | |
74 | struct wm831x_on *wm831x_on; | |
cd99758b | 75 | int irq = wm831x_irq(wm831x, platform_get_irq(pdev, 0)); |
0c73b992 MB |
76 | int ret; |
77 | ||
dae6ba4a MB |
78 | wm831x_on = devm_kzalloc(&pdev->dev, sizeof(struct wm831x_on), |
79 | GFP_KERNEL); | |
0c73b992 MB |
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 | ||
5d3caa44 | 88 | wm831x_on->dev = devm_input_allocate_device(&pdev->dev); |
0c73b992 MB |
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 | 101 | ret = request_threaded_irq(irq, NULL, wm831x_on_irq, |
ca8ff6ac FE |
102 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
103 | "wm831x_on", | |
afadb8e0 | 104 | wm831x_on); |
0c73b992 MB |
105 | if (ret < 0) { |
106 | dev_err(&pdev->dev, "Unable to request IRQ: %d\n", ret); | |
107 | goto err_input_dev; | |
108 | } | |
109 | ret = input_register_device(wm831x_on->dev); | |
110 | if (ret) { | |
111 | dev_dbg(&pdev->dev, "Can't register input device: %d\n", ret); | |
112 | goto err_irq; | |
113 | } | |
114 | ||
115 | platform_set_drvdata(pdev, wm831x_on); | |
116 | ||
117 | return 0; | |
118 | ||
119 | err_irq: | |
afadb8e0 | 120 | free_irq(irq, wm831x_on); |
0c73b992 | 121 | err_input_dev: |
0c73b992 | 122 | err: |
0c73b992 MB |
123 | return ret; |
124 | } | |
125 | ||
e2619cf7 | 126 | static int wm831x_on_remove(struct platform_device *pdev) |
0c73b992 MB |
127 | { |
128 | struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); | |
129 | int irq = platform_get_irq(pdev, 0); | |
130 | ||
afadb8e0 | 131 | free_irq(irq, wm831x_on); |
0c73b992 | 132 | cancel_delayed_work_sync(&wm831x_on->work); |
0c73b992 MB |
133 | |
134 | return 0; | |
135 | } | |
136 | ||
137 | static struct platform_driver wm831x_on_driver = { | |
138 | .probe = wm831x_on_probe, | |
1cb0aa88 | 139 | .remove = wm831x_on_remove, |
0c73b992 MB |
140 | .driver = { |
141 | .name = "wm831x-on", | |
0c73b992 MB |
142 | }, |
143 | }; | |
840a746b | 144 | module_platform_driver(wm831x_on_driver); |
0c73b992 MB |
145 | |
146 | MODULE_ALIAS("platform:wm831x-on"); | |
147 | MODULE_DESCRIPTION("WM831x ON pin"); | |
148 | MODULE_LICENSE("GPL"); | |
149 | MODULE_AUTHOR("Mark Brown <broonie@opensource.wolfsonmicro.com>"); | |
150 |