aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/input/misc
diff options
context:
space:
mode:
authorCarlo Caione <carlo@caione.org>2014-12-29 14:20:54 -0500
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2015-01-08 16:53:51 -0500
commit5b6c26a9f6437c4467f5d7cbf0313a7ae2be86b1 (patch)
tree375a3c783a93930c81a673fab90df505a08966e5 /drivers/input/misc
parent2c50ad340c246b7f58f2d916006afe2d85d60698 (diff)
Input: add driver for AXP20x Power Enable Key
This change adds support for the Power Enable Key found on MFD AXP202 and AXP209. Besides the basic support for the button, the driver adds two entries in sysfs to configure the time delay for power on/off. Signed-off-by: Carlo Caione <carlo@caione.org> Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com> [wens@csie.org: made axp20x_pek_remove() static; removed driver owner field; fixed path for sysfs entries] Signed-off-by: Chen-Yu Tsai <wens@csie.org> Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r--drivers/input/misc/Kconfig11
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/axp20x-pek.c282
3 files changed, 294 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 95919170795d..9e610c4862a2 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -426,6 +426,17 @@ config INPUT_TPS65218_PWRBUTTON
426 To compile this driver as a module, choose M here. The module will 426 To compile this driver as a module, choose M here. The module will
427 be called tps65218-pwrbutton. 427 be called tps65218-pwrbutton.
428 428
429config INPUT_AXP20X_PEK
430 tristate "X-Powers AXP20X power button driver"
431 depends on MFD_AXP20X
432 help
433 Say Y here if you want to enable power key reporting via the
434 AXP20X PMIC.
435
436 To compile this driver as a module, choose M here. The module will
437 be called axp20x-pek.
438
439
429config INPUT_TWL4030_PWRBUTTON 440config INPUT_TWL4030_PWRBUTTON
430 tristate "TWL4030 Power button Driver" 441 tristate "TWL4030 Power button Driver"
431 depends on TWL4030_CORE 442 depends on TWL4030_CORE
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 9a1083f78374..f8d9ea7c059a 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -55,6 +55,7 @@ obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o
55obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o 55obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o
56obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o 56obj-$(CONFIG_INPUT_REGULATOR_HAPTIC) += regulator-haptic.o
57obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o 57obj-$(CONFIG_INPUT_RETU_PWRBUTTON) += retu-pwrbutton.o
58obj-$(CONFIG_INPUT_AXP20X_PEK) += axp20x-pek.o
58obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o 59obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o
59obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o 60obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
60obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o 61obj-$(CONFIG_INPUT_SIRFSOC_ONKEY) += sirfsoc-onkey.o
diff --git a/drivers/input/misc/axp20x-pek.c b/drivers/input/misc/axp20x-pek.c
new file mode 100644
index 000000000000..8dbd097ac939
--- /dev/null
+++ b/drivers/input/misc/axp20x-pek.c
@@ -0,0 +1,282 @@
1/*
2 * axp20x power button driver.
3 *
4 * Copyright (C) 2013 Carlo Caione <carlo@caione.org>
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
16#include <linux/errno.h>
17#include <linux/irq.h>
18#include <linux/init.h>
19#include <linux/input.h>
20#include <linux/interrupt.h>
21#include <linux/kernel.h>
22#include <linux/mfd/axp20x.h>
23#include <linux/module.h>
24#include <linux/platform_device.h>
25#include <linux/regmap.h>
26#include <linux/slab.h>
27
28#define AXP20X_PEK_STARTUP_MASK (0xc0)
29#define AXP20X_PEK_SHUTDOWN_MASK (0x03)
30
31struct axp20x_pek {
32 struct axp20x_dev *axp20x;
33 struct input_dev *input;
34 int irq_dbr;
35 int irq_dbf;
36};
37
38struct axp20x_time {
39 unsigned int time;
40 unsigned int idx;
41};
42
43static const struct axp20x_time startup_time[] = {
44 { .time = 128, .idx = 0 },
45 { .time = 1000, .idx = 2 },
46 { .time = 3000, .idx = 1 },
47 { .time = 2000, .idx = 3 },
48};
49
50static const struct axp20x_time shutdown_time[] = {
51 { .time = 4000, .idx = 0 },
52 { .time = 6000, .idx = 1 },
53 { .time = 8000, .idx = 2 },
54 { .time = 10000, .idx = 3 },
55};
56
57struct axp20x_pek_ext_attr {
58 const struct axp20x_time *p_time;
59 unsigned int mask;
60};
61
62static struct axp20x_pek_ext_attr axp20x_pek_startup_ext_attr = {
63 .p_time = startup_time,
64 .mask = AXP20X_PEK_STARTUP_MASK,
65};
66
67static struct axp20x_pek_ext_attr axp20x_pek_shutdown_ext_attr = {
68 .p_time = shutdown_time,
69 .mask = AXP20X_PEK_SHUTDOWN_MASK,
70};
71
72static struct axp20x_pek_ext_attr *get_axp_ext_attr(struct device_attribute *attr)
73{
74 return container_of(attr, struct dev_ext_attribute, attr)->var;
75}
76
77static ssize_t axp20x_show_ext_attr(struct device *dev,
78 struct device_attribute *attr, char *buf)
79{
80 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
81 struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
82 unsigned int val;
83 int ret, i;
84
85 ret = regmap_read(axp20x_pek->axp20x->regmap, AXP20X_PEK_KEY, &val);
86 if (ret != 0)
87 return ret;
88
89 val &= axp20x_ea->mask;
90 val >>= ffs(axp20x_ea->mask) - 1;
91
92 for (i = 0; i < 4; i++)
93 if (val == axp20x_ea->p_time[i].idx)
94 val = axp20x_ea->p_time[i].time;
95
96 return sprintf(buf, "%u\n", val);
97}
98
99static ssize_t axp20x_store_ext_attr(struct device *dev,
100 struct device_attribute *attr,
101 const char *buf, size_t count)
102{
103 struct axp20x_pek *axp20x_pek = dev_get_drvdata(dev);
104 struct axp20x_pek_ext_attr *axp20x_ea = get_axp_ext_attr(attr);
105 char val_str[20];
106 size_t len;
107 int ret, i;
108 unsigned int val, idx = 0;
109 unsigned int best_err = UINT_MAX;
110
111 val_str[sizeof(val_str) - 1] = '\0';
112 strncpy(val_str, buf, sizeof(val_str) - 1);
113 len = strlen(val_str);
114
115 if (len && val_str[len - 1] == '\n')
116 val_str[len - 1] = '\0';
117
118 ret = kstrtouint(val_str, 10, &val);
119 if (ret)
120 return ret;
121
122 for (i = 3; i >= 0; i--) {
123 unsigned int err;
124
125 err = abs(axp20x_ea->p_time[i].time - val);
126 if (err < best_err) {
127 best_err = err;
128 idx = axp20x_ea->p_time[i].idx;
129 }
130
131 if (!err)
132 break;
133 }
134
135 idx <<= ffs(axp20x_ea->mask) - 1;
136 ret = regmap_update_bits(axp20x_pek->axp20x->regmap,
137 AXP20X_PEK_KEY,
138 axp20x_ea->mask, idx);
139 if (ret != 0)
140 return -EINVAL;
141 return count;
142}
143
144static struct dev_ext_attribute axp20x_dev_attr_startup = {
145 .attr = __ATTR(startup, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
146 .var = &axp20x_pek_startup_ext_attr
147};
148
149static struct dev_ext_attribute axp20x_dev_attr_shutdown = {
150 .attr = __ATTR(shutdown, 0644, axp20x_show_ext_attr, axp20x_store_ext_attr),
151 .var = &axp20x_pek_shutdown_ext_attr
152};
153
154static irqreturn_t axp20x_pek_irq(int irq, void *pwr)
155{
156 struct input_dev *idev = pwr;
157 struct axp20x_pek *axp20x_pek = input_get_drvdata(idev);
158
159 if (irq == axp20x_pek->irq_dbr)
160 input_report_key(idev, KEY_POWER, true);
161 else if (irq == axp20x_pek->irq_dbf)
162 input_report_key(idev, KEY_POWER, false);
163
164 input_sync(idev);
165
166 return IRQ_HANDLED;
167}
168
169static int axp20x_pek_probe(struct platform_device *pdev)
170{
171 struct axp20x_pek *axp20x_pek;
172 struct axp20x_dev *axp20x;
173 struct input_dev *idev;
174 int error;
175
176 axp20x_pek = devm_kzalloc(&pdev->dev, sizeof(struct axp20x_pek),
177 GFP_KERNEL);
178 if (!axp20x_pek)
179 return -ENOMEM;
180
181 axp20x_pek->axp20x = dev_get_drvdata(pdev->dev.parent);
182 axp20x = axp20x_pek->axp20x;
183
184 axp20x_pek->irq_dbr = platform_get_irq_byname(pdev, "PEK_DBR");
185 if (axp20x_pek->irq_dbr < 0) {
186 dev_err(&pdev->dev, "No IRQ for PEK_DBR, error=%d\n",
187 axp20x_pek->irq_dbr);
188 return axp20x_pek->irq_dbr;
189 }
190 axp20x_pek->irq_dbr = regmap_irq_get_virq(axp20x->regmap_irqc,
191 axp20x_pek->irq_dbr);
192
193 axp20x_pek->irq_dbf = platform_get_irq_byname(pdev, "PEK_DBF");
194 if (axp20x_pek->irq_dbf < 0) {
195 dev_err(&pdev->dev, "No IRQ for PEK_DBF, error=%d\n",
196 axp20x_pek->irq_dbf);
197 return axp20x_pek->irq_dbf;
198 }
199 axp20x_pek->irq_dbf = regmap_irq_get_virq(axp20x->regmap_irqc,
200 axp20x_pek->irq_dbf);
201
202 axp20x_pek->input = devm_input_allocate_device(&pdev->dev);
203 if (!axp20x_pek->input)
204 return -ENOMEM;
205
206 idev = axp20x_pek->input;
207
208 idev->name = "axp20x-pek";
209 idev->phys = "m1kbd/input2";
210 idev->dev.parent = &pdev->dev;
211
212 input_set_capability(idev, EV_KEY, KEY_POWER);
213
214 input_set_drvdata(idev, axp20x_pek);
215
216 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbr,
217 axp20x_pek_irq, 0,
218 "axp20x-pek-dbr", idev);
219 if (error < 0) {
220 dev_err(axp20x->dev, "Failed to request dbr IRQ#%d: %d\n",
221 axp20x_pek->irq_dbr, error);
222
223 return error;
224 }
225
226 error = devm_request_any_context_irq(&pdev->dev, axp20x_pek->irq_dbf,
227 axp20x_pek_irq, 0,
228 "axp20x-pek-dbf", idev);
229 if (error < 0) {
230 dev_err(axp20x->dev, "Failed to request dbf IRQ#%d: %d\n",
231 axp20x_pek->irq_dbf, error);
232 return error;
233 }
234
235 error = device_create_file(&pdev->dev, &axp20x_dev_attr_startup.attr);
236 if (error)
237 return error;
238
239 error = device_create_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr);
240 if (error)
241 goto clear_startup_attr;
242
243 error = input_register_device(idev);
244 if (error) {
245 dev_err(axp20x->dev, "Can't register input device: %d\n",
246 error);
247 goto clear_attr;
248 }
249
250 platform_set_drvdata(pdev, axp20x_pek);
251
252 return 0;
253
254clear_attr:
255 device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr);
256
257clear_startup_attr:
258 device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr);
259
260 return error;
261}
262
263static int axp20x_pek_remove(struct platform_device *pdev)
264{
265 device_remove_file(&pdev->dev, &axp20x_dev_attr_shutdown.attr);
266 device_remove_file(&pdev->dev, &axp20x_dev_attr_startup.attr);
267
268 return 0;
269}
270
271static struct platform_driver axp20x_pek_driver = {
272 .probe = axp20x_pek_probe,
273 .remove = axp20x_pek_remove,
274 .driver = {
275 .name = "axp20x-pek",
276 },
277};
278module_platform_driver(axp20x_pek_driver);
279
280MODULE_DESCRIPTION("axp20x Power Button");
281MODULE_AUTHOR("Carlo Caione <carlo@caione.org>");
282MODULE_LICENSE("GPL");