xref: /openbmc/linux/drivers/input/keyboard/gpio_keys.c (revision a33466e31213cd7c46696160d3db78680b58f6a3)
178a56aabSPhil Blundell /*
278a56aabSPhil Blundell  * Driver for keys on GPIO lines capable of generating interrupts.
378a56aabSPhil Blundell  *
478a56aabSPhil Blundell  * Copyright 2005 Phil Blundell
578a56aabSPhil Blundell  *
678a56aabSPhil Blundell  * This program is free software; you can redistribute it and/or modify
778a56aabSPhil Blundell  * it under the terms of the GNU General Public License version 2 as
878a56aabSPhil Blundell  * published by the Free Software Foundation.
978a56aabSPhil Blundell  */
1078a56aabSPhil Blundell 
1178a56aabSPhil Blundell #include <linux/module.h>
1278a56aabSPhil Blundell #include <linux/version.h>
1378a56aabSPhil Blundell 
1478a56aabSPhil Blundell #include <linux/init.h>
1578a56aabSPhil Blundell #include <linux/fs.h>
1678a56aabSPhil Blundell #include <linux/interrupt.h>
1778a56aabSPhil Blundell #include <linux/irq.h>
1878a56aabSPhil Blundell #include <linux/sched.h>
1978a56aabSPhil Blundell #include <linux/pm.h>
2078a56aabSPhil Blundell #include <linux/sysctl.h>
2178a56aabSPhil Blundell #include <linux/proc_fs.h>
2278a56aabSPhil Blundell #include <linux/delay.h>
2378a56aabSPhil Blundell #include <linux/platform_device.h>
2478a56aabSPhil Blundell #include <linux/input.h>
2549015beeSDavid Brownell #include <linux/gpio_keys.h>
2678a56aabSPhil Blundell 
270d98f6bbSPhilipp Zabel #include <asm/gpio.h>
2878a56aabSPhil Blundell 
29*a33466e3SDmitry Baryshkov struct gpio_button_data {
30*a33466e3SDmitry Baryshkov 	struct gpio_keys_button *button;
31*a33466e3SDmitry Baryshkov 	struct input_dev *input;
32*a33466e3SDmitry Baryshkov 	struct timer_list timer;
33*a33466e3SDmitry Baryshkov };
34*a33466e3SDmitry Baryshkov 
35*a33466e3SDmitry Baryshkov struct gpio_keys_drvdata {
36*a33466e3SDmitry Baryshkov 	struct input_dev *input;
37*a33466e3SDmitry Baryshkov 	struct gpio_button_data data[0];
38*a33466e3SDmitry Baryshkov };
39*a33466e3SDmitry Baryshkov 
40*a33466e3SDmitry Baryshkov static void gpio_keys_report_event(struct gpio_keys_button *button,
41*a33466e3SDmitry Baryshkov 				   struct input_dev *input)
4278a56aabSPhil Blundell {
4384767d00SRoman Moravcik 	unsigned int type = button->type ?: EV_KEY;
44*a33466e3SDmitry Baryshkov 	int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
4584767d00SRoman Moravcik 
4684767d00SRoman Moravcik 	input_event(input, type, button->code, !!state);
4778a56aabSPhil Blundell 	input_sync(input);
48*a33466e3SDmitry Baryshkov }
49*a33466e3SDmitry Baryshkov 
50*a33466e3SDmitry Baryshkov static void gpio_check_button(unsigned long _data)
51*a33466e3SDmitry Baryshkov {
52*a33466e3SDmitry Baryshkov 	struct gpio_button_data *data = (struct gpio_button_data *)_data;
53*a33466e3SDmitry Baryshkov 
54*a33466e3SDmitry Baryshkov 	gpio_keys_report_event(data->button, data->input);
55*a33466e3SDmitry Baryshkov }
56*a33466e3SDmitry Baryshkov 
57*a33466e3SDmitry Baryshkov static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
58*a33466e3SDmitry Baryshkov {
59*a33466e3SDmitry Baryshkov 	struct platform_device *pdev = dev_id;
60*a33466e3SDmitry Baryshkov 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
61*a33466e3SDmitry Baryshkov 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
62*a33466e3SDmitry Baryshkov 	int i;
63*a33466e3SDmitry Baryshkov 
64*a33466e3SDmitry Baryshkov 	for (i = 0; i < pdata->nbuttons; i++) {
65*a33466e3SDmitry Baryshkov 		struct gpio_keys_button *button = &pdata->buttons[i];
66*a33466e3SDmitry Baryshkov 
67*a33466e3SDmitry Baryshkov 		if (irq == gpio_to_irq(button->gpio)) {
68*a33466e3SDmitry Baryshkov 			struct gpio_button_data *bdata = &ddata->data[i];
69*a33466e3SDmitry Baryshkov 
70*a33466e3SDmitry Baryshkov 			if (button->debounce_interval)
71*a33466e3SDmitry Baryshkov 				mod_timer(&bdata->timer,
72*a33466e3SDmitry Baryshkov 					  jiffies +
73*a33466e3SDmitry Baryshkov 					  msecs_to_jiffies(button->debounce_interval));
74*a33466e3SDmitry Baryshkov 			else
75*a33466e3SDmitry Baryshkov 				gpio_keys_report_event(button, bdata->input);
76*a33466e3SDmitry Baryshkov 
771164ec1aSDavid Brownell 			return IRQ_HANDLED;
7878a56aabSPhil Blundell 		}
7978a56aabSPhil Blundell 	}
8078a56aabSPhil Blundell 
811164ec1aSDavid Brownell 	return IRQ_NONE;
8278a56aabSPhil Blundell }
8378a56aabSPhil Blundell 
8478a56aabSPhil Blundell static int __devinit gpio_keys_probe(struct platform_device *pdev)
8578a56aabSPhil Blundell {
8678a56aabSPhil Blundell 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
87*a33466e3SDmitry Baryshkov 	struct gpio_keys_drvdata *ddata;
8878a56aabSPhil Blundell 	struct input_dev *input;
8978a56aabSPhil Blundell 	int i, error;
90e15b0213SAnti Sullin 	int wakeup = 0;
9178a56aabSPhil Blundell 
92*a33466e3SDmitry Baryshkov 	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
93*a33466e3SDmitry Baryshkov 			pdata->nbuttons * sizeof(struct gpio_button_data),
94*a33466e3SDmitry Baryshkov 			GFP_KERNEL);
9578a56aabSPhil Blundell 	input = input_allocate_device();
96*a33466e3SDmitry Baryshkov 	if (!ddata || !input) {
97*a33466e3SDmitry Baryshkov 		error = -ENOMEM;
98*a33466e3SDmitry Baryshkov 		goto fail1;
99*a33466e3SDmitry Baryshkov 	}
10078a56aabSPhil Blundell 
101*a33466e3SDmitry Baryshkov 	platform_set_drvdata(pdev, ddata);
10278a56aabSPhil Blundell 
10378a56aabSPhil Blundell 	input->name = pdev->name;
10478a56aabSPhil Blundell 	input->phys = "gpio-keys/input0";
105469ba4dfSDmitry Torokhov 	input->dev.parent = &pdev->dev;
10678a56aabSPhil Blundell 
10778a56aabSPhil Blundell 	input->id.bustype = BUS_HOST;
10878a56aabSPhil Blundell 	input->id.vendor = 0x0001;
10978a56aabSPhil Blundell 	input->id.product = 0x0001;
11078a56aabSPhil Blundell 	input->id.version = 0x0100;
11178a56aabSPhil Blundell 
112*a33466e3SDmitry Baryshkov 	ddata->input = input;
113*a33466e3SDmitry Baryshkov 
11478a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
11584767d00SRoman Moravcik 		struct gpio_keys_button *button = &pdata->buttons[i];
116*a33466e3SDmitry Baryshkov 		struct gpio_button_data *bdata = &ddata->data[i];
1176a2e3911SHerbert Valerio Riedel 		int irq;
11884767d00SRoman Moravcik 		unsigned int type = button->type ?: EV_KEY;
11978a56aabSPhil Blundell 
120*a33466e3SDmitry Baryshkov 		bdata->input = input;
121*a33466e3SDmitry Baryshkov 		setup_timer(&bdata->timer,
122*a33466e3SDmitry Baryshkov 			    gpio_check_button, (unsigned long)bdata);
123*a33466e3SDmitry Baryshkov 
1246a2e3911SHerbert Valerio Riedel 		error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
1256a2e3911SHerbert Valerio Riedel 		if (error < 0) {
1266a2e3911SHerbert Valerio Riedel 			pr_err("gpio-keys: failed to request GPIO %d,"
1276a2e3911SHerbert Valerio Riedel 				" error %d\n", button->gpio, error);
128*a33466e3SDmitry Baryshkov 			goto fail2;
1296a2e3911SHerbert Valerio Riedel 		}
1306a2e3911SHerbert Valerio Riedel 
1316a2e3911SHerbert Valerio Riedel 		error = gpio_direction_input(button->gpio);
1326a2e3911SHerbert Valerio Riedel 		if (error < 0) {
1336a2e3911SHerbert Valerio Riedel 			pr_err("gpio-keys: failed to configure input"
1346a2e3911SHerbert Valerio Riedel 				" direction for GPIO %d, error %d\n",
1356a2e3911SHerbert Valerio Riedel 				button->gpio, error);
1366a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
137*a33466e3SDmitry Baryshkov 			goto fail2;
1386a2e3911SHerbert Valerio Riedel 		}
1396a2e3911SHerbert Valerio Riedel 
1406a2e3911SHerbert Valerio Riedel 		irq = gpio_to_irq(button->gpio);
141006df302SAnti Sullin 		if (irq < 0) {
142006df302SAnti Sullin 			error = irq;
1436a2e3911SHerbert Valerio Riedel 			pr_err("gpio-keys: Unable to get irq number"
1446a2e3911SHerbert Valerio Riedel 				" for GPIO %d, error %d\n",
145006df302SAnti Sullin 				button->gpio, error);
1466a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
147*a33466e3SDmitry Baryshkov 			goto fail2;
148006df302SAnti Sullin 		}
149006df302SAnti Sullin 
150006df302SAnti Sullin 		error = request_irq(irq, gpio_keys_isr,
151006df302SAnti Sullin 				    IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
152006df302SAnti Sullin 					IRQF_TRIGGER_FALLING,
15384767d00SRoman Moravcik 				    button->desc ? button->desc : "gpio_keys",
15478a56aabSPhil Blundell 				    pdev);
15578a56aabSPhil Blundell 		if (error) {
1566a2e3911SHerbert Valerio Riedel 			pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
1570d98f6bbSPhilipp Zabel 				irq, error);
1586a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
159*a33466e3SDmitry Baryshkov 			goto fail2;
16078a56aabSPhil Blundell 		}
16184767d00SRoman Moravcik 
162e15b0213SAnti Sullin 		if (button->wakeup)
163e15b0213SAnti Sullin 			wakeup = 1;
164e15b0213SAnti Sullin 
16584767d00SRoman Moravcik 		input_set_capability(input, type, button->code);
16678a56aabSPhil Blundell 	}
16778a56aabSPhil Blundell 
16878a56aabSPhil Blundell 	error = input_register_device(input);
16978a56aabSPhil Blundell 	if (error) {
1706a2e3911SHerbert Valerio Riedel 		pr_err("gpio-keys: Unable to register input device, "
171006df302SAnti Sullin 			"error: %d\n", error);
172*a33466e3SDmitry Baryshkov 		goto fail2;
17378a56aabSPhil Blundell 	}
17478a56aabSPhil Blundell 
175e15b0213SAnti Sullin 	device_init_wakeup(&pdev->dev, wakeup);
176e15b0213SAnti Sullin 
17778a56aabSPhil Blundell 	return 0;
17878a56aabSPhil Blundell 
179*a33466e3SDmitry Baryshkov  fail2:
1806a2e3911SHerbert Valerio Riedel 	while (--i >= 0) {
1810d98f6bbSPhilipp Zabel 		free_irq(gpio_to_irq(pdata->buttons[i].gpio), pdev);
182*a33466e3SDmitry Baryshkov 		if (pdata->buttons[i].debounce_interval)
183*a33466e3SDmitry Baryshkov 			del_timer_sync(&ddata->data[i].timer);
1846a2e3911SHerbert Valerio Riedel 		gpio_free(pdata->buttons[i].gpio);
1856a2e3911SHerbert Valerio Riedel 	}
18678a56aabSPhil Blundell 
187006df302SAnti Sullin 	platform_set_drvdata(pdev, NULL);
188*a33466e3SDmitry Baryshkov  fail1:
18978a56aabSPhil Blundell 	input_free_device(input);
190*a33466e3SDmitry Baryshkov 	kfree(ddata);
19178a56aabSPhil Blundell 
19278a56aabSPhil Blundell 	return error;
19378a56aabSPhil Blundell }
19478a56aabSPhil Blundell 
19578a56aabSPhil Blundell static int __devexit gpio_keys_remove(struct platform_device *pdev)
19678a56aabSPhil Blundell {
19778a56aabSPhil Blundell 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
198*a33466e3SDmitry Baryshkov 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
199*a33466e3SDmitry Baryshkov 	struct input_dev *input = ddata->input;
20078a56aabSPhil Blundell 	int i;
20178a56aabSPhil Blundell 
202e15b0213SAnti Sullin 	device_init_wakeup(&pdev->dev, 0);
203e15b0213SAnti Sullin 
20478a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
2050d98f6bbSPhilipp Zabel 		int irq = gpio_to_irq(pdata->buttons[i].gpio);
20678a56aabSPhil Blundell 		free_irq(irq, pdev);
207*a33466e3SDmitry Baryshkov 		if (pdata->buttons[i].debounce_interval)
208*a33466e3SDmitry Baryshkov 			del_timer_sync(&ddata->data[i].timer);
2096a2e3911SHerbert Valerio Riedel 		gpio_free(pdata->buttons[i].gpio);
21078a56aabSPhil Blundell 	}
21178a56aabSPhil Blundell 
21278a56aabSPhil Blundell 	input_unregister_device(input);
21378a56aabSPhil Blundell 
21478a56aabSPhil Blundell 	return 0;
21578a56aabSPhil Blundell }
21678a56aabSPhil Blundell 
217e15b0213SAnti Sullin 
218e15b0213SAnti Sullin #ifdef CONFIG_PM
219e15b0213SAnti Sullin static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
220e15b0213SAnti Sullin {
221e15b0213SAnti Sullin 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
222e15b0213SAnti Sullin 	int i;
223e15b0213SAnti Sullin 
224e15b0213SAnti Sullin 	if (device_may_wakeup(&pdev->dev)) {
225e15b0213SAnti Sullin 		for (i = 0; i < pdata->nbuttons; i++) {
226e15b0213SAnti Sullin 			struct gpio_keys_button *button = &pdata->buttons[i];
227e15b0213SAnti Sullin 			if (button->wakeup) {
228e15b0213SAnti Sullin 				int irq = gpio_to_irq(button->gpio);
229e15b0213SAnti Sullin 				enable_irq_wake(irq);
230e15b0213SAnti Sullin 			}
231e15b0213SAnti Sullin 		}
232e15b0213SAnti Sullin 	}
233e15b0213SAnti Sullin 
234e15b0213SAnti Sullin 	return 0;
235e15b0213SAnti Sullin }
236e15b0213SAnti Sullin 
237e15b0213SAnti Sullin static int gpio_keys_resume(struct platform_device *pdev)
238e15b0213SAnti Sullin {
239e15b0213SAnti Sullin 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
240e15b0213SAnti Sullin 	int i;
241e15b0213SAnti Sullin 
242e15b0213SAnti Sullin 	if (device_may_wakeup(&pdev->dev)) {
243e15b0213SAnti Sullin 		for (i = 0; i < pdata->nbuttons; i++) {
244e15b0213SAnti Sullin 			struct gpio_keys_button *button = &pdata->buttons[i];
245e15b0213SAnti Sullin 			if (button->wakeup) {
246e15b0213SAnti Sullin 				int irq = gpio_to_irq(button->gpio);
247e15b0213SAnti Sullin 				disable_irq_wake(irq);
248e15b0213SAnti Sullin 			}
249e15b0213SAnti Sullin 		}
250e15b0213SAnti Sullin 	}
251e15b0213SAnti Sullin 
252e15b0213SAnti Sullin 	return 0;
253e15b0213SAnti Sullin }
254e15b0213SAnti Sullin #else
255e15b0213SAnti Sullin #define gpio_keys_suspend	NULL
256e15b0213SAnti Sullin #define gpio_keys_resume	NULL
257e15b0213SAnti Sullin #endif
258e15b0213SAnti Sullin 
25978a56aabSPhil Blundell struct platform_driver gpio_keys_device_driver = {
26078a56aabSPhil Blundell 	.probe		= gpio_keys_probe,
26178a56aabSPhil Blundell 	.remove		= __devexit_p(gpio_keys_remove),
262e15b0213SAnti Sullin 	.suspend	= gpio_keys_suspend,
263e15b0213SAnti Sullin 	.resume		= gpio_keys_resume,
26478a56aabSPhil Blundell 	.driver		= {
26578a56aabSPhil Blundell 		.name	= "gpio-keys",
266d7b5247bSKay Sievers 		.owner	= THIS_MODULE,
26778a56aabSPhil Blundell 	}
26878a56aabSPhil Blundell };
26978a56aabSPhil Blundell 
27078a56aabSPhil Blundell static int __init gpio_keys_init(void)
27178a56aabSPhil Blundell {
27278a56aabSPhil Blundell 	return platform_driver_register(&gpio_keys_device_driver);
27378a56aabSPhil Blundell }
27478a56aabSPhil Blundell 
27578a56aabSPhil Blundell static void __exit gpio_keys_exit(void)
27678a56aabSPhil Blundell {
27778a56aabSPhil Blundell 	platform_driver_unregister(&gpio_keys_device_driver);
27878a56aabSPhil Blundell }
27978a56aabSPhil Blundell 
28078a56aabSPhil Blundell module_init(gpio_keys_init);
28178a56aabSPhil Blundell module_exit(gpio_keys_exit);
28278a56aabSPhil Blundell 
28378a56aabSPhil Blundell MODULE_LICENSE("GPL");
28478a56aabSPhil Blundell MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
28578a56aabSPhil Blundell MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
286d7b5247bSKay Sievers MODULE_ALIAS("platform:gpio-keys");
287