]>
Commit | Line | Data |
---|---|---|
92d57a73 TS |
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>"); |