diff options
author | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
---|---|---|
committer | Jonathan Herman <hermanjl@cs.unc.edu> | 2013-01-22 10:38:37 -0500 |
commit | fcc9d2e5a6c89d22b8b773a64fb4ad21ac318446 (patch) | |
tree | a57612d1888735a2ec7972891b68c1ac5ec8faea /drivers/input/misc/gpio_input.c | |
parent | 8dea78da5cee153b8af9c07a2745f6c55057fe12 (diff) |
Diffstat (limited to 'drivers/input/misc/gpio_input.c')
-rw-r--r-- | drivers/input/misc/gpio_input.c | 376 |
1 files changed, 376 insertions, 0 deletions
diff --git a/drivers/input/misc/gpio_input.c b/drivers/input/misc/gpio_input.c new file mode 100644 index 00000000000..6a0c3151096 --- /dev/null +++ b/drivers/input/misc/gpio_input.c | |||
@@ -0,0 +1,376 @@ | |||
1 | /* drivers/input/misc/gpio_input.c | ||
2 | * | ||
3 | * Copyright (C) 2007 Google, Inc. | ||
4 | * | ||
5 | * This software is licensed under the terms of the GNU General Public | ||
6 | * License version 2, as published by the Free Software Foundation, and | ||
7 | * may be copied, distributed, and modified under those terms. | ||
8 | * | ||
9 | * This program is distributed in the hope that it will be useful, | ||
10 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
11 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
12 | * GNU General Public License for more details. | ||
13 | * | ||
14 | */ | ||
15 | |||
16 | #include <linux/kernel.h> | ||
17 | #include <linux/gpio.h> | ||
18 | #include <linux/gpio_event.h> | ||
19 | #include <linux/hrtimer.h> | ||
20 | #include <linux/input.h> | ||
21 | #include <linux/interrupt.h> | ||
22 | #include <linux/slab.h> | ||
23 | #include <linux/wakelock.h> | ||
24 | |||
25 | enum { | ||
26 | DEBOUNCE_UNSTABLE = BIT(0), /* Got irq, while debouncing */ | ||
27 | DEBOUNCE_PRESSED = BIT(1), | ||
28 | DEBOUNCE_NOTPRESSED = BIT(2), | ||
29 | DEBOUNCE_WAIT_IRQ = BIT(3), /* Stable irq state */ | ||
30 | DEBOUNCE_POLL = BIT(4), /* Stable polling state */ | ||
31 | |||
32 | DEBOUNCE_UNKNOWN = | ||
33 | DEBOUNCE_PRESSED | DEBOUNCE_NOTPRESSED, | ||
34 | }; | ||
35 | |||
36 | struct gpio_key_state { | ||
37 | struct gpio_input_state *ds; | ||
38 | uint8_t debounce; | ||
39 | }; | ||
40 | |||
41 | struct gpio_input_state { | ||
42 | struct gpio_event_input_devs *input_devs; | ||
43 | const struct gpio_event_input_info *info; | ||
44 | struct hrtimer timer; | ||
45 | int use_irq; | ||
46 | int debounce_count; | ||
47 | spinlock_t irq_lock; | ||
48 | struct wake_lock wake_lock; | ||
49 | struct gpio_key_state key_state[0]; | ||
50 | }; | ||
51 | |||
52 | static enum hrtimer_restart gpio_event_input_timer_func(struct hrtimer *timer) | ||
53 | { | ||
54 | int i; | ||
55 | int pressed; | ||
56 | struct gpio_input_state *ds = | ||
57 | container_of(timer, struct gpio_input_state, timer); | ||
58 | unsigned gpio_flags = ds->info->flags; | ||
59 | unsigned npolarity; | ||
60 | int nkeys = ds->info->keymap_size; | ||
61 | const struct gpio_event_direct_entry *key_entry; | ||
62 | struct gpio_key_state *key_state; | ||
63 | unsigned long irqflags; | ||
64 | uint8_t debounce; | ||
65 | bool sync_needed; | ||
66 | |||
67 | #if 0 | ||
68 | key_entry = kp->keys_info->keymap; | ||
69 | key_state = kp->key_state; | ||
70 | for (i = 0; i < nkeys; i++, key_entry++, key_state++) | ||
71 | pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, | ||
72 | gpio_read_detect_status(key_entry->gpio)); | ||
73 | #endif | ||
74 | key_entry = ds->info->keymap; | ||
75 | key_state = ds->key_state; | ||
76 | sync_needed = false; | ||
77 | spin_lock_irqsave(&ds->irq_lock, irqflags); | ||
78 | for (i = 0; i < nkeys; i++, key_entry++, key_state++) { | ||
79 | debounce = key_state->debounce; | ||
80 | if (debounce & DEBOUNCE_WAIT_IRQ) | ||
81 | continue; | ||
82 | if (key_state->debounce & DEBOUNCE_UNSTABLE) { | ||
83 | debounce = key_state->debounce = DEBOUNCE_UNKNOWN; | ||
84 | enable_irq(gpio_to_irq(key_entry->gpio)); | ||
85 | if (gpio_flags & GPIOEDF_PRINT_KEY_UNSTABLE) | ||
86 | pr_info("gpio_keys_scan_keys: key %x-%x, %d " | ||
87 | "(%d) continue debounce\n", | ||
88 | ds->info->type, key_entry->code, | ||
89 | i, key_entry->gpio); | ||
90 | } | ||
91 | npolarity = !(gpio_flags & GPIOEDF_ACTIVE_HIGH); | ||
92 | pressed = gpio_get_value(key_entry->gpio) ^ npolarity; | ||
93 | if (debounce & DEBOUNCE_POLL) { | ||
94 | if (pressed == !(debounce & DEBOUNCE_PRESSED)) { | ||
95 | ds->debounce_count++; | ||
96 | key_state->debounce = DEBOUNCE_UNKNOWN; | ||
97 | if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) | ||
98 | pr_info("gpio_keys_scan_keys: key %x-" | ||
99 | "%x, %d (%d) start debounce\n", | ||
100 | ds->info->type, key_entry->code, | ||
101 | i, key_entry->gpio); | ||
102 | } | ||
103 | continue; | ||
104 | } | ||
105 | if (pressed && (debounce & DEBOUNCE_NOTPRESSED)) { | ||
106 | if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) | ||
107 | pr_info("gpio_keys_scan_keys: key %x-%x, %d " | ||
108 | "(%d) debounce pressed 1\n", | ||
109 | ds->info->type, key_entry->code, | ||
110 | i, key_entry->gpio); | ||
111 | key_state->debounce = DEBOUNCE_PRESSED; | ||
112 | continue; | ||
113 | } | ||
114 | if (!pressed && (debounce & DEBOUNCE_PRESSED)) { | ||
115 | if (gpio_flags & GPIOEDF_PRINT_KEY_DEBOUNCE) | ||
116 | pr_info("gpio_keys_scan_keys: key %x-%x, %d " | ||
117 | "(%d) debounce pressed 0\n", | ||
118 | ds->info->type, key_entry->code, | ||
119 | i, key_entry->gpio); | ||
120 | key_state->debounce = DEBOUNCE_NOTPRESSED; | ||
121 | continue; | ||
122 | } | ||
123 | /* key is stable */ | ||
124 | ds->debounce_count--; | ||
125 | if (ds->use_irq) | ||
126 | key_state->debounce |= DEBOUNCE_WAIT_IRQ; | ||
127 | else | ||
128 | key_state->debounce |= DEBOUNCE_POLL; | ||
129 | if (gpio_flags & GPIOEDF_PRINT_KEYS) | ||
130 | pr_info("gpio_keys_scan_keys: key %x-%x, %d (%d) " | ||
131 | "changed to %d\n", ds->info->type, | ||
132 | key_entry->code, i, key_entry->gpio, pressed); | ||
133 | input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, | ||
134 | key_entry->code, pressed); | ||
135 | sync_needed = true; | ||
136 | } | ||
137 | if (sync_needed) { | ||
138 | for (i = 0; i < ds->input_devs->count; i++) | ||
139 | input_sync(ds->input_devs->dev[i]); | ||
140 | } | ||
141 | |||
142 | #if 0 | ||
143 | key_entry = kp->keys_info->keymap; | ||
144 | key_state = kp->key_state; | ||
145 | for (i = 0; i < nkeys; i++, key_entry++, key_state++) { | ||
146 | pr_info("gpio_read_detect_status %d %d\n", key_entry->gpio, | ||
147 | gpio_read_detect_status(key_entry->gpio)); | ||
148 | } | ||
149 | #endif | ||
150 | |||
151 | if (ds->debounce_count) | ||
152 | hrtimer_start(timer, ds->info->debounce_time, HRTIMER_MODE_REL); | ||
153 | else if (!ds->use_irq) | ||
154 | hrtimer_start(timer, ds->info->poll_time, HRTIMER_MODE_REL); | ||
155 | else | ||
156 | wake_unlock(&ds->wake_lock); | ||
157 | |||
158 | spin_unlock_irqrestore(&ds->irq_lock, irqflags); | ||
159 | |||
160 | return HRTIMER_NORESTART; | ||
161 | } | ||
162 | |||
163 | static irqreturn_t gpio_event_input_irq_handler(int irq, void *dev_id) | ||
164 | { | ||
165 | struct gpio_key_state *ks = dev_id; | ||
166 | struct gpio_input_state *ds = ks->ds; | ||
167 | int keymap_index = ks - ds->key_state; | ||
168 | const struct gpio_event_direct_entry *key_entry; | ||
169 | unsigned long irqflags; | ||
170 | int pressed; | ||
171 | |||
172 | if (!ds->use_irq) | ||
173 | return IRQ_HANDLED; | ||
174 | |||
175 | key_entry = &ds->info->keymap[keymap_index]; | ||
176 | |||
177 | if (ds->info->debounce_time.tv64) { | ||
178 | spin_lock_irqsave(&ds->irq_lock, irqflags); | ||
179 | if (ks->debounce & DEBOUNCE_WAIT_IRQ) { | ||
180 | ks->debounce = DEBOUNCE_UNKNOWN; | ||
181 | if (ds->debounce_count++ == 0) { | ||
182 | wake_lock(&ds->wake_lock); | ||
183 | hrtimer_start( | ||
184 | &ds->timer, ds->info->debounce_time, | ||
185 | HRTIMER_MODE_REL); | ||
186 | } | ||
187 | if (ds->info->flags & GPIOEDF_PRINT_KEY_DEBOUNCE) | ||
188 | pr_info("gpio_event_input_irq_handler: " | ||
189 | "key %x-%x, %d (%d) start debounce\n", | ||
190 | ds->info->type, key_entry->code, | ||
191 | keymap_index, key_entry->gpio); | ||
192 | } else { | ||
193 | disable_irq_nosync(irq); | ||
194 | ks->debounce = DEBOUNCE_UNSTABLE; | ||
195 | } | ||
196 | spin_unlock_irqrestore(&ds->irq_lock, irqflags); | ||
197 | } else { | ||
198 | pressed = gpio_get_value(key_entry->gpio) ^ | ||
199 | !(ds->info->flags & GPIOEDF_ACTIVE_HIGH); | ||
200 | if (ds->info->flags & GPIOEDF_PRINT_KEYS) | ||
201 | pr_info("gpio_event_input_irq_handler: key %x-%x, %d " | ||
202 | "(%d) changed to %d\n", | ||
203 | ds->info->type, key_entry->code, keymap_index, | ||
204 | key_entry->gpio, pressed); | ||
205 | input_event(ds->input_devs->dev[key_entry->dev], ds->info->type, | ||
206 | key_entry->code, pressed); | ||
207 | input_sync(ds->input_devs->dev[key_entry->dev]); | ||
208 | } | ||
209 | return IRQ_HANDLED; | ||
210 | } | ||
211 | |||
212 | static int gpio_event_input_request_irqs(struct gpio_input_state *ds) | ||
213 | { | ||
214 | int i; | ||
215 | int err; | ||
216 | unsigned int irq; | ||
217 | unsigned long req_flags = IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING; | ||
218 | |||
219 | for (i = 0; i < ds->info->keymap_size; i++) { | ||
220 | err = irq = gpio_to_irq(ds->info->keymap[i].gpio); | ||
221 | if (err < 0) | ||
222 | goto err_gpio_get_irq_num_failed; | ||
223 | err = request_irq(irq, gpio_event_input_irq_handler, | ||
224 | req_flags, "gpio_keys", &ds->key_state[i]); | ||
225 | if (err) { | ||
226 | pr_err("gpio_event_input_request_irqs: request_irq " | ||
227 | "failed for input %d, irq %d\n", | ||
228 | ds->info->keymap[i].gpio, irq); | ||
229 | goto err_request_irq_failed; | ||
230 | } | ||
231 | if (ds->info->info.no_suspend) { | ||
232 | err = enable_irq_wake(irq); | ||
233 | if (err) { | ||
234 | pr_err("gpio_event_input_request_irqs: " | ||
235 | "enable_irq_wake failed for input %d, " | ||
236 | "irq %d\n", | ||
237 | ds->info->keymap[i].gpio, irq); | ||
238 | goto err_enable_irq_wake_failed; | ||
239 | } | ||
240 | } | ||
241 | } | ||
242 | return 0; | ||
243 | |||
244 | for (i = ds->info->keymap_size - 1; i >= 0; i--) { | ||
245 | irq = gpio_to_irq(ds->info->keymap[i].gpio); | ||
246 | if (ds->info->info.no_suspend) | ||
247 | disable_irq_wake(irq); | ||
248 | err_enable_irq_wake_failed: | ||
249 | free_irq(irq, &ds->key_state[i]); | ||
250 | err_request_irq_failed: | ||
251 | err_gpio_get_irq_num_failed: | ||
252 | ; | ||
253 | } | ||
254 | return err; | ||
255 | } | ||
256 | |||
257 | int gpio_event_input_func(struct gpio_event_input_devs *input_devs, | ||
258 | struct gpio_event_info *info, void **data, int func) | ||
259 | { | ||
260 | int ret; | ||
261 | int i; | ||
262 | unsigned long irqflags; | ||
263 | struct gpio_event_input_info *di; | ||
264 | struct gpio_input_state *ds = *data; | ||
265 | |||
266 | di = container_of(info, struct gpio_event_input_info, info); | ||
267 | |||
268 | if (func == GPIO_EVENT_FUNC_SUSPEND) { | ||
269 | if (ds->use_irq) | ||
270 | for (i = 0; i < di->keymap_size; i++) | ||
271 | disable_irq(gpio_to_irq(di->keymap[i].gpio)); | ||
272 | hrtimer_cancel(&ds->timer); | ||
273 | return 0; | ||
274 | } | ||
275 | if (func == GPIO_EVENT_FUNC_RESUME) { | ||
276 | spin_lock_irqsave(&ds->irq_lock, irqflags); | ||
277 | if (ds->use_irq) | ||
278 | for (i = 0; i < di->keymap_size; i++) | ||
279 | enable_irq(gpio_to_irq(di->keymap[i].gpio)); | ||
280 | hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); | ||
281 | spin_unlock_irqrestore(&ds->irq_lock, irqflags); | ||
282 | return 0; | ||
283 | } | ||
284 | |||
285 | if (func == GPIO_EVENT_FUNC_INIT) { | ||
286 | if (ktime_to_ns(di->poll_time) <= 0) | ||
287 | di->poll_time = ktime_set(0, 20 * NSEC_PER_MSEC); | ||
288 | |||
289 | *data = ds = kzalloc(sizeof(*ds) + sizeof(ds->key_state[0]) * | ||
290 | di->keymap_size, GFP_KERNEL); | ||
291 | if (ds == NULL) { | ||
292 | ret = -ENOMEM; | ||
293 | pr_err("gpio_event_input_func: " | ||
294 | "Failed to allocate private data\n"); | ||
295 | goto err_ds_alloc_failed; | ||
296 | } | ||
297 | ds->debounce_count = di->keymap_size; | ||
298 | ds->input_devs = input_devs; | ||
299 | ds->info = di; | ||
300 | wake_lock_init(&ds->wake_lock, WAKE_LOCK_SUSPEND, "gpio_input"); | ||
301 | spin_lock_init(&ds->irq_lock); | ||
302 | |||
303 | for (i = 0; i < di->keymap_size; i++) { | ||
304 | int dev = di->keymap[i].dev; | ||
305 | if (dev >= input_devs->count) { | ||
306 | pr_err("gpio_event_input_func: bad device " | ||
307 | "index %d >= %d for key code %d\n", | ||
308 | dev, input_devs->count, | ||
309 | di->keymap[i].code); | ||
310 | ret = -EINVAL; | ||
311 | goto err_bad_keymap; | ||
312 | } | ||
313 | input_set_capability(input_devs->dev[dev], di->type, | ||
314 | di->keymap[i].code); | ||
315 | ds->key_state[i].ds = ds; | ||
316 | ds->key_state[i].debounce = DEBOUNCE_UNKNOWN; | ||
317 | } | ||
318 | |||
319 | for (i = 0; i < di->keymap_size; i++) { | ||
320 | ret = gpio_request(di->keymap[i].gpio, "gpio_kp_in"); | ||
321 | if (ret) { | ||
322 | pr_err("gpio_event_input_func: gpio_request " | ||
323 | "failed for %d\n", di->keymap[i].gpio); | ||
324 | goto err_gpio_request_failed; | ||
325 | } | ||
326 | ret = gpio_direction_input(di->keymap[i].gpio); | ||
327 | if (ret) { | ||
328 | pr_err("gpio_event_input_func: " | ||
329 | "gpio_direction_input failed for %d\n", | ||
330 | di->keymap[i].gpio); | ||
331 | goto err_gpio_configure_failed; | ||
332 | } | ||
333 | } | ||
334 | |||
335 | ret = gpio_event_input_request_irqs(ds); | ||
336 | |||
337 | spin_lock_irqsave(&ds->irq_lock, irqflags); | ||
338 | ds->use_irq = ret == 0; | ||
339 | |||
340 | pr_info("GPIO Input Driver: Start gpio inputs for %s%s in %s " | ||
341 | "mode\n", input_devs->dev[0]->name, | ||
342 | (input_devs->count > 1) ? "..." : "", | ||
343 | ret == 0 ? "interrupt" : "polling"); | ||
344 | |||
345 | hrtimer_init(&ds->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); | ||
346 | ds->timer.function = gpio_event_input_timer_func; | ||
347 | hrtimer_start(&ds->timer, ktime_set(0, 0), HRTIMER_MODE_REL); | ||
348 | spin_unlock_irqrestore(&ds->irq_lock, irqflags); | ||
349 | return 0; | ||
350 | } | ||
351 | |||
352 | ret = 0; | ||
353 | spin_lock_irqsave(&ds->irq_lock, irqflags); | ||
354 | hrtimer_cancel(&ds->timer); | ||
355 | if (ds->use_irq) { | ||
356 | for (i = di->keymap_size - 1; i >= 0; i--) { | ||
357 | int irq = gpio_to_irq(di->keymap[i].gpio); | ||
358 | if (ds->info->info.no_suspend) | ||
359 | disable_irq_wake(irq); | ||
360 | free_irq(irq, &ds->key_state[i]); | ||
361 | } | ||
362 | } | ||
363 | spin_unlock_irqrestore(&ds->irq_lock, irqflags); | ||
364 | |||
365 | for (i = di->keymap_size - 1; i >= 0; i--) { | ||
366 | err_gpio_configure_failed: | ||
367 | gpio_free(di->keymap[i].gpio); | ||
368 | err_gpio_request_failed: | ||
369 | ; | ||
370 | } | ||
371 | err_bad_keymap: | ||
372 | wake_lock_destroy(&ds->wake_lock); | ||
373 | kfree(ds); | ||
374 | err_ds_alloc_failed: | ||
375 | return ret; | ||
376 | } | ||