2 * Keyboard driver for Openmoko Freerunner GSM phone
4 * (C) 2006-2007 by Openmoko, Inc.
5 * Author: Harald Welte <laforge@openmoko.org>
8 * inspired by corkgbd.c by Richard Purdie
10 * This program is free software; you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License version 2 as
12 * published by the Free Software Foundation.
16 #include <linux/delay.h>
17 #include <linux/platform_device.h>
18 #include <linux/init.h>
19 #include <linux/input.h>
20 #include <linux/interrupt.h>
21 #include <linux/jiffies.h>
22 #include <linux/module.h>
23 #include <linux/slab.h>
25 #include <mach/gpio.h>
26 #include <asm/mach-types.h>
29 extern int global_inside_suspend
;
31 #define global_inside_suspend 0
35 struct platform_device
*pdev
;
36 struct input_dev
*input
;
47 irqreturn_t (*isr
)(int irq
, void *dev_id
);
52 static irqreturn_t
gta02kbd_aux_irq(int irq
, void *dev_id
);
53 static irqreturn_t
gta02kbd_default_key_irq(int irq
, void *dev_id
);
56 static struct gta02kbd_key keys
[] = {
58 .name
= "GTA02 AUX button",
59 .isr
= gta02kbd_aux_irq
,
60 .input_key
= KEY_PHONE
,
63 .name
= "GTA02 HOLD button",
64 .isr
= gta02kbd_default_key_irq
,
65 .input_key
= KEY_PAUSE
,
69 /* This timer section filters AUX button IRQ bouncing */
71 static void aux_key_timer_f(unsigned long data
);
73 static struct timer_list aux_key_timer
=
74 TIMER_INITIALIZER(aux_key_timer_f
, 0, 0);
76 #define AUX_TIMER_TIMEOUT (HZ >> 7)
77 #define AUX_TIMER_ALLOWED_NOOP 2
78 #define AUX_TIMER_CONSECUTIVE_EVENTS 5
80 struct gta02kbd
*timer_kbd
;
82 static void aux_key_timer_f(unsigned long data
)
84 static int noop_counter
;
85 static int last_key
= -1;
86 static int last_count
;
90 gpio_get_value(timer_kbd
->pdev
->resource
[GTA02_KEY_AUX
].start
);
92 if (likely(key_pressed
== last_key
))
96 last_key
= key_pressed
;
99 if (unlikely(last_count
>= AUX_TIMER_CONSECUTIVE_EVENTS
)) {
100 if (timer_kbd
->aux_state
!= last_key
) {
101 input_report_key(timer_kbd
->input
, KEY_PHONE
, last_key
);
102 input_sync(timer_kbd
->input
);
104 timer_kbd
->aux_state
= last_key
;
108 if (unlikely(++noop_counter
> AUX_TIMER_ALLOWED_NOOP
)) {
114 mod_timer(&aux_key_timer
, jiffies
+ AUX_TIMER_TIMEOUT
);
117 static irqreturn_t
gta02kbd_aux_irq(int irq
, void *dev
)
119 mod_timer(&aux_key_timer
, jiffies
+ AUX_TIMER_TIMEOUT
);
124 static irqreturn_t
gta02kbd_default_key_irq(int irq
, void *dev_id
)
126 struct gta02kbd
*kbd
= dev_id
;
129 for (n
= 0; n
< ARRAY_SIZE(keys
); n
++) {
131 if (irq
!= keys
[n
].irq
)
134 input_report_key(kbd
->input
, keys
[n
].input_key
,
135 gpio_get_value(kbd
->pdev
->resource
[n
].start
));
136 input_sync(kbd
->input
);
143 static int gta02kbd_suspend(struct platform_device
*dev
, pm_message_t state
)
145 disable_irq(keys
[GTA02_KEY_AUX
].irq
);
146 del_timer_sync(&aux_key_timer
);
150 static int gta02kbd_resume(struct platform_device
*dev
)
152 enable_irq(keys
[GTA02_KEY_AUX
].irq
);
156 #define gta02kbd_suspend NULL
157 #define gta02kbd_resume NULL
160 static int gta02kbd_probe(struct platform_device
*pdev
)
162 struct gta02kbd
*gta02kbd
;
163 struct input_dev
*input_dev
;
168 if (pdev
->resource
[0].flags
!= 0)
171 gta02kbd
= kzalloc(sizeof(struct gta02kbd
), GFP_KERNEL
);
172 input_dev
= input_allocate_device();
173 if (!gta02kbd
|| !input_dev
) {
175 input_free_device(input_dev
);
179 gta02kbd
->pdev
= pdev
;
180 timer_kbd
= gta02kbd
;
182 platform_set_drvdata(pdev
, gta02kbd
);
184 gta02kbd
->input
= input_dev
;
186 input_dev
->name
= "GTA02 Buttons";
187 input_dev
->phys
= "gta02kbd/input0";
188 input_dev
->id
.bustype
= BUS_HOST
;
189 input_dev
->id
.vendor
= 0x0001;
190 input_dev
->id
.product
= 0x0001;
191 input_dev
->id
.version
= 0x0100;
192 input_dev
->dev
.parent
= &pdev
->dev
;
194 input_dev
->evbit
[0] = BIT(EV_KEY
) | BIT(EV_SW
);
195 set_bit(KEY_PHONE
, input_dev
->keybit
);
196 set_bit(KEY_PAUSE
, input_dev
->keybit
);
198 rc
= input_register_device(gta02kbd
->input
);
202 /* register GPIO IRQs */
203 for(n
= 0; n
< min(pdev
->num_resources
, ARRAY_SIZE(keys
)); n
++) {
205 if (!pdev
->resource
[0].start
)
208 irq
= gpio_to_irq(pdev
->resource
[n
].start
);
212 if (request_irq(irq
, keys
[n
].isr
, IRQF_DISABLED
|
213 IRQF_TRIGGER_RISING
| IRQF_TRIGGER_FALLING
,
214 keys
[n
].name
, gta02kbd
)) {
215 dev_err(&pdev
->dev
, "Can't get IRQ %u\n", irq
);
217 /* unwind any irq registrations and fail */
221 free_irq(gpio_to_irq(pdev
->resource
[n
].start
),
224 goto out_device_create
;
234 input_unregister_device(gta02kbd
->input
);
236 input_free_device(gta02kbd
->input
);
237 platform_set_drvdata(pdev
, NULL
);
243 static int gta02kbd_remove(struct platform_device
*pdev
)
245 struct gta02kbd
*gta02kbd
= platform_get_drvdata(pdev
);
247 free_irq(gpio_to_irq(pdev
->resource
[1].start
), gta02kbd
);
248 free_irq(gpio_to_irq(pdev
->resource
[0].start
), gta02kbd
);
250 input_unregister_device(gta02kbd
->input
);
251 input_free_device(gta02kbd
->input
);
252 platform_set_drvdata(pdev
, NULL
);
258 static struct platform_driver gta02kbd_driver
= {
259 .probe
= gta02kbd_probe
,
260 .remove
= gta02kbd_remove
,
261 .suspend
= gta02kbd_suspend
,
262 .resume
= gta02kbd_resume
,
264 .name
= "gta02-button",
268 static int __devinit
gta02kbd_init(void)
270 return platform_driver_register(>a02kbd_driver
);
273 static void __exit
gta02kbd_exit(void)
275 platform_driver_unregister(>a02kbd_driver
);
278 module_init(gta02kbd_init
);
279 module_exit(gta02kbd_exit
);
281 MODULE_AUTHOR("Harald Welte <laforge@openmoko.org>");
282 MODULE_DESCRIPTION("Openmoko Freerunner buttons input driver");
283 MODULE_LICENSE("GPL");