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/slab.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(dev: wm831x_on->dev, KEY_POWER, value: poll); |
52 | input_sync(dev: 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(dwork: &wm831x_on->work, delay: 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(dwork: &wm831x_on->work, delay: 0); |
67 | |
68 | return IRQ_HANDLED; |
69 | } |
70 | |
71 | static int wm831x_on_probe(struct platform_device *pdev) |
72 | { |
73 | struct wm831x *wm831x = dev_get_drvdata(dev: pdev->dev.parent); |
74 | struct wm831x_on *wm831x_on; |
75 | int irq = wm831x_irq(wm831x, irq: platform_get_irq(pdev, 0)); |
76 | int ret; |
77 | |
78 | wm831x_on = devm_kzalloc(dev: &pdev->dev, size: sizeof(struct wm831x_on), |
79 | 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 = devm_input_allocate_device(&pdev->dev); |
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 | |
101 | ret = request_threaded_irq(irq, NULL, thread_fn: wm831x_on_irq, |
102 | IRQF_TRIGGER_RISING | IRQF_ONESHOT, |
103 | name: "wm831x_on" , |
104 | dev: wm831x_on); |
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, data: wm831x_on); |
116 | |
117 | return 0; |
118 | |
119 | err_irq: |
120 | free_irq(irq, wm831x_on); |
121 | err_input_dev: |
122 | err: |
123 | return ret; |
124 | } |
125 | |
126 | static int wm831x_on_remove(struct platform_device *pdev) |
127 | { |
128 | struct wm831x_on *wm831x_on = platform_get_drvdata(pdev); |
129 | int irq = platform_get_irq(pdev, 0); |
130 | |
131 | free_irq(irq, wm831x_on); |
132 | cancel_delayed_work_sync(dwork: &wm831x_on->work); |
133 | |
134 | return 0; |
135 | } |
136 | |
137 | static struct platform_driver wm831x_on_driver = { |
138 | .probe = wm831x_on_probe, |
139 | .remove = wm831x_on_remove, |
140 | .driver = { |
141 | .name = "wm831x-on" , |
142 | }, |
143 | }; |
144 | module_platform_driver(wm831x_on_driver); |
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 | |
151 | |