diff options
author | Trilok Soni <tsoni@codeaurora.org> | 2011-05-13 05:47:51 -0400 |
---|---|---|
committer | Samuel Ortiz <sameo@linux.intel.com> | 2011-05-26 13:45:54 -0400 |
commit | 92d57a73e41047bff7d0812e06f893567876d455 (patch) | |
tree | 2002ca05d97293126187b8662a73ac4ae9c0ed74 /drivers/input | |
parent | 39325b59d88b42ba2ccf2e62c234059e9941a47c (diff) |
input: Add support for Qualcomm PMIC8XXX power key
Add support for PMIC8XXX power key driven over dedicated
KYPD_PWR_N pin.
Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/misc/Kconfig | 11 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/pmic8xxx-pwrkey.c | 231 |
3 files changed, 243 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index f9cf0881b0e3..45dc6aa62ba4 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig | |||
@@ -330,6 +330,17 @@ config INPUT_PWM_BEEPER | |||
330 | To compile this driver as a module, choose M here: the module will be | 330 | To compile this driver as a module, choose M here: the module will be |
331 | called pwm-beeper. | 331 | called pwm-beeper. |
332 | 332 | ||
333 | config INPUT_PMIC8XXX_PWRKEY | ||
334 | tristate "PMIC8XXX power key support" | ||
335 | depends on MFD_PM8XXX | ||
336 | help | ||
337 | Say Y here if you want support for the PMIC8XXX power key. | ||
338 | |||
339 | If unsure, say N. | ||
340 | |||
341 | To compile this driver as a module, choose M here: the | ||
342 | module will be called pmic8xxx-pwrkey. | ||
343 | |||
333 | config INPUT_GPIO_ROTARY_ENCODER | 344 | config INPUT_GPIO_ROTARY_ENCODER |
334 | tristate "Rotary encoders connected to GPIO pins" | 345 | tristate "Rotary encoders connected to GPIO pins" |
335 | depends on GPIOLIB && GENERIC_GPIO | 346 | depends on GPIOLIB && GENERIC_GPIO |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index e3f7984e6274..38efb2cb182b 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile | |||
@@ -33,6 +33,7 @@ obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o | |||
33 | obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o | 33 | obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o |
34 | obj-$(CONFIG_INPUT_POWERMATE) += powermate.o | 34 | obj-$(CONFIG_INPUT_POWERMATE) += powermate.o |
35 | obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o | 35 | obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o |
36 | obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o | ||
36 | obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o | 37 | obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o |
37 | obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o | 38 | obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o |
38 | obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o | 39 | obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o |
diff --git a/drivers/input/misc/pmic8xxx-pwrkey.c b/drivers/input/misc/pmic8xxx-pwrkey.c new file mode 100644 index 000000000000..97e07e786e41 --- /dev/null +++ b/drivers/input/misc/pmic8xxx-pwrkey.c | |||
@@ -0,0 +1,231 @@ | |||
1 | /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. | ||
2 | * | ||
3 | * This program is free software; you can redistribute it and/or modify | ||
4 | * it under the terms of the GNU General Public License version 2 and | ||
5 | * only version 2 as published by the Free Software Foundation. | ||
6 | * | ||
7 | * This program is distributed in the hope that it will be useful, | ||
8 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
9 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
10 | * GNU General Public License for more details. | ||
11 | */ | ||
12 | |||
13 | #include <linux/module.h> | ||
14 | #include <linux/init.h> | ||
15 | #include <linux/kernel.h> | ||
16 | #include <linux/errno.h> | ||
17 | #include <linux/slab.h> | ||
18 | #include <linux/input.h> | ||
19 | #include <linux/interrupt.h> | ||
20 | #include <linux/platform_device.h> | ||
21 | #include <linux/log2.h> | ||
22 | |||
23 | #include <linux/mfd/pm8xxx/core.h> | ||
24 | #include <linux/input/pmic8xxx-pwrkey.h> | ||
25 | |||
26 | #define PON_CNTL_1 0x1C | ||
27 | #define PON_CNTL_PULL_UP BIT(7) | ||
28 | #define PON_CNTL_TRIG_DELAY_MASK (0x7) | ||
29 | |||
30 | /** | ||
31 | * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information | ||
32 | * @key_press_irq: key press irq number | ||
33 | */ | ||
34 | struct pmic8xxx_pwrkey { | ||
35 | struct input_dev *pwr; | ||
36 | int key_press_irq; | ||
37 | }; | ||
38 | |||
39 | static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) | ||
40 | { | ||
41 | struct pmic8xxx_pwrkey *pwrkey = _pwrkey; | ||
42 | |||
43 | input_report_key(pwrkey->pwr, KEY_POWER, 1); | ||
44 | input_sync(pwrkey->pwr); | ||
45 | |||
46 | return IRQ_HANDLED; | ||
47 | } | ||
48 | |||
49 | static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) | ||
50 | { | ||
51 | struct pmic8xxx_pwrkey *pwrkey = _pwrkey; | ||
52 | |||
53 | input_report_key(pwrkey->pwr, KEY_POWER, 0); | ||
54 | input_sync(pwrkey->pwr); | ||
55 | |||
56 | return IRQ_HANDLED; | ||
57 | } | ||
58 | |||
59 | #ifdef CONFIG_PM_SLEEP | ||
60 | static int pmic8xxx_pwrkey_suspend(struct device *dev) | ||
61 | { | ||
62 | struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); | ||
63 | |||
64 | if (device_may_wakeup(dev)) | ||
65 | enable_irq_wake(pwrkey->key_press_irq); | ||
66 | |||
67 | return 0; | ||
68 | } | ||
69 | |||
70 | static int pmic8xxx_pwrkey_resume(struct device *dev) | ||
71 | { | ||
72 | struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); | ||
73 | |||
74 | if (device_may_wakeup(dev)) | ||
75 | disable_irq_wake(pwrkey->key_press_irq); | ||
76 | |||
77 | return 0; | ||
78 | } | ||
79 | #endif | ||
80 | |||
81 | static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, | ||
82 | pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); | ||
83 | |||
84 | static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) | ||
85 | { | ||
86 | struct input_dev *pwr; | ||
87 | int key_release_irq = platform_get_irq(pdev, 0); | ||
88 | int key_press_irq = platform_get_irq(pdev, 1); | ||
89 | int err; | ||
90 | unsigned int delay; | ||
91 | u8 pon_cntl; | ||
92 | struct pmic8xxx_pwrkey *pwrkey; | ||
93 | const struct pm8xxx_pwrkey_platform_data *pdata = mfd_get_data(pdev); | ||
94 | |||
95 | if (!pdata) { | ||
96 | dev_err(&pdev->dev, "power key platform data not supplied\n"); | ||
97 | return -EINVAL; | ||
98 | } | ||
99 | |||
100 | if (pdata->kpd_trigger_delay_us > 62500) { | ||
101 | dev_err(&pdev->dev, "invalid power key trigger delay\n"); | ||
102 | return -EINVAL; | ||
103 | } | ||
104 | |||
105 | pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); | ||
106 | if (!pwrkey) | ||
107 | return -ENOMEM; | ||
108 | |||
109 | pwr = input_allocate_device(); | ||
110 | if (!pwr) { | ||
111 | dev_dbg(&pdev->dev, "Can't allocate power button\n"); | ||
112 | err = -ENOMEM; | ||
113 | goto free_pwrkey; | ||
114 | } | ||
115 | |||
116 | input_set_capability(pwr, EV_KEY, KEY_POWER); | ||
117 | |||
118 | pwr->name = "pmic8xxx_pwrkey"; | ||
119 | pwr->phys = "pmic8xxx_pwrkey/input0"; | ||
120 | pwr->dev.parent = &pdev->dev; | ||
121 | |||
122 | delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; | ||
123 | delay = 1 + ilog2(delay); | ||
124 | |||
125 | err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); | ||
126 | if (err < 0) { | ||
127 | dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); | ||
128 | goto free_input_dev; | ||
129 | } | ||
130 | |||
131 | pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; | ||
132 | pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); | ||
133 | if (pdata->pull_up) | ||
134 | pon_cntl |= PON_CNTL_PULL_UP; | ||
135 | else | ||
136 | pon_cntl &= ~PON_CNTL_PULL_UP; | ||
137 | |||
138 | err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); | ||
139 | if (err < 0) { | ||
140 | dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); | ||
141 | goto free_input_dev; | ||
142 | } | ||
143 | |||
144 | err = input_register_device(pwr); | ||
145 | if (err) { | ||
146 | dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); | ||
147 | goto free_input_dev; | ||
148 | } | ||
149 | |||
150 | pwrkey->key_press_irq = key_press_irq; | ||
151 | pwrkey->pwr = pwr; | ||
152 | |||
153 | platform_set_drvdata(pdev, pwrkey); | ||
154 | |||
155 | err = request_irq(key_press_irq, pwrkey_press_irq, | ||
156 | IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); | ||
157 | if (err < 0) { | ||
158 | dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", | ||
159 | key_press_irq, err); | ||
160 | goto unreg_input_dev; | ||
161 | } | ||
162 | |||
163 | err = request_irq(key_release_irq, pwrkey_release_irq, | ||
164 | IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); | ||
165 | if (err < 0) { | ||
166 | dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", | ||
167 | key_release_irq, err); | ||
168 | |||
169 | goto free_press_irq; | ||
170 | } | ||
171 | |||
172 | device_init_wakeup(&pdev->dev, pdata->wakeup); | ||
173 | |||
174 | return 0; | ||
175 | |||
176 | free_press_irq: | ||
177 | free_irq(key_press_irq, NULL); | ||
178 | unreg_input_dev: | ||
179 | platform_set_drvdata(pdev, NULL); | ||
180 | input_unregister_device(pwr); | ||
181 | pwr = NULL; | ||
182 | free_input_dev: | ||
183 | input_free_device(pwr); | ||
184 | free_pwrkey: | ||
185 | kfree(pwrkey); | ||
186 | return err; | ||
187 | } | ||
188 | |||
189 | static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) | ||
190 | { | ||
191 | struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); | ||
192 | int key_release_irq = platform_get_irq(pdev, 0); | ||
193 | int key_press_irq = platform_get_irq(pdev, 1); | ||
194 | |||
195 | device_init_wakeup(&pdev->dev, 0); | ||
196 | |||
197 | free_irq(key_press_irq, pwrkey); | ||
198 | free_irq(key_release_irq, pwrkey); | ||
199 | input_unregister_device(pwrkey->pwr); | ||
200 | platform_set_drvdata(pdev, NULL); | ||
201 | kfree(pwrkey); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static struct platform_driver pmic8xxx_pwrkey_driver = { | ||
207 | .probe = pmic8xxx_pwrkey_probe, | ||
208 | .remove = __devexit_p(pmic8xxx_pwrkey_remove), | ||
209 | .driver = { | ||
210 | .name = PM8XXX_PWRKEY_DEV_NAME, | ||
211 | .owner = THIS_MODULE, | ||
212 | .pm = &pm8xxx_pwr_key_pm_ops, | ||
213 | }, | ||
214 | }; | ||
215 | |||
216 | static int __init pmic8xxx_pwrkey_init(void) | ||
217 | { | ||
218 | return platform_driver_register(&pmic8xxx_pwrkey_driver); | ||
219 | } | ||
220 | module_init(pmic8xxx_pwrkey_init); | ||
221 | |||
222 | static void __exit pmic8xxx_pwrkey_exit(void) | ||
223 | { | ||
224 | platform_driver_unregister(&pmic8xxx_pwrkey_driver); | ||
225 | } | ||
226 | module_exit(pmic8xxx_pwrkey_exit); | ||
227 | |||
228 | MODULE_ALIAS("platform:pmic8xxx_pwrkey"); | ||
229 | MODULE_DESCRIPTION("PMIC8XXX Power Key driver"); | ||
230 | MODULE_LICENSE("GPL v2"); | ||
231 | MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>"); | ||