xref: /openbmc/linux/drivers/input/keyboard/gpio_keys.c (revision db19fd8b3a3e198e84b93fa217acf77e72a4cd35)
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 
1378a56aabSPhil Blundell #include <linux/init.h>
1478a56aabSPhil Blundell #include <linux/fs.h>
1578a56aabSPhil Blundell #include <linux/interrupt.h>
1678a56aabSPhil Blundell #include <linux/irq.h>
1778a56aabSPhil Blundell #include <linux/sched.h>
1878a56aabSPhil Blundell #include <linux/pm.h>
1978a56aabSPhil Blundell #include <linux/sysctl.h>
2078a56aabSPhil Blundell #include <linux/proc_fs.h>
2178a56aabSPhil Blundell #include <linux/delay.h>
2278a56aabSPhil Blundell #include <linux/platform_device.h>
2378a56aabSPhil Blundell #include <linux/input.h>
2449015beeSDavid Brownell #include <linux/gpio_keys.h>
25da0d03feSJani Nikula #include <linux/workqueue.h>
2678a56aabSPhil Blundell 
270d98f6bbSPhilipp Zabel #include <asm/gpio.h>
2878a56aabSPhil Blundell 
29a33466e3SDmitry Baryshkov struct gpio_button_data {
30a33466e3SDmitry Baryshkov 	struct gpio_keys_button *button;
31a33466e3SDmitry Baryshkov 	struct input_dev *input;
32ca865a77SJani Nikula 	struct timer_list timer;
33da0d03feSJani Nikula 	struct work_struct work;
34a33466e3SDmitry Baryshkov };
35a33466e3SDmitry Baryshkov 
36a33466e3SDmitry Baryshkov struct gpio_keys_drvdata {
37a33466e3SDmitry Baryshkov 	struct input_dev *input;
38a33466e3SDmitry Baryshkov 	struct gpio_button_data data[0];
39a33466e3SDmitry Baryshkov };
40a33466e3SDmitry Baryshkov 
41da0d03feSJani Nikula static void gpio_keys_report_event(struct work_struct *work)
4278a56aabSPhil Blundell {
43da0d03feSJani Nikula 	struct gpio_button_data *bdata =
44da0d03feSJani Nikula 		container_of(work, struct gpio_button_data, work);
45ce25d7e9SUwe Kleine-König 	struct gpio_keys_button *button = bdata->button;
46ce25d7e9SUwe Kleine-König 	struct input_dev *input = bdata->input;
4784767d00SRoman Moravcik 	unsigned int type = button->type ?: EV_KEY;
48a33466e3SDmitry Baryshkov 	int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
4984767d00SRoman Moravcik 
5084767d00SRoman Moravcik 	input_event(input, type, button->code, !!state);
5178a56aabSPhil Blundell 	input_sync(input);
52a33466e3SDmitry Baryshkov }
53a33466e3SDmitry Baryshkov 
54da0d03feSJani Nikula static void gpio_keys_timer(unsigned long _data)
55ca865a77SJani Nikula {
56ca865a77SJani Nikula 	struct gpio_button_data *data = (struct gpio_button_data *)_data;
57ca865a77SJani Nikula 
58da0d03feSJani Nikula 	schedule_work(&data->work);
59ca865a77SJani Nikula }
60ca865a77SJani Nikula 
61a33466e3SDmitry Baryshkov static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
62a33466e3SDmitry Baryshkov {
6357ffe9d5SUwe Kleine-König 	struct gpio_button_data *bdata = dev_id;
6457ffe9d5SUwe Kleine-König 	struct gpio_keys_button *button = bdata->button;
65a33466e3SDmitry Baryshkov 
6657ffe9d5SUwe Kleine-König 	BUG_ON(irq != gpio_to_irq(button->gpio));
67a33466e3SDmitry Baryshkov 
68ca865a77SJani Nikula 	if (button->debounce_interval)
69ca865a77SJani Nikula 		mod_timer(&bdata->timer,
70ca865a77SJani Nikula 			jiffies + msecs_to_jiffies(button->debounce_interval));
71ca865a77SJani Nikula 	else
72da0d03feSJani Nikula 		schedule_work(&bdata->work);
73a33466e3SDmitry Baryshkov 
741164ec1aSDavid Brownell 	return IRQ_HANDLED;
7578a56aabSPhil Blundell }
7678a56aabSPhil Blundell 
7778a56aabSPhil Blundell static int __devinit gpio_keys_probe(struct platform_device *pdev)
7878a56aabSPhil Blundell {
7978a56aabSPhil Blundell 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
80a33466e3SDmitry Baryshkov 	struct gpio_keys_drvdata *ddata;
81*db19fd8bSBen Dooks 	struct device *dev = &pdev->dev;
8278a56aabSPhil Blundell 	struct input_dev *input;
8378a56aabSPhil Blundell 	int i, error;
84e15b0213SAnti Sullin 	int wakeup = 0;
8578a56aabSPhil Blundell 
86a33466e3SDmitry Baryshkov 	ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
87a33466e3SDmitry Baryshkov 			pdata->nbuttons * sizeof(struct gpio_button_data),
88a33466e3SDmitry Baryshkov 			GFP_KERNEL);
8978a56aabSPhil Blundell 	input = input_allocate_device();
90a33466e3SDmitry Baryshkov 	if (!ddata || !input) {
91*db19fd8bSBen Dooks 		dev_err(dev, "failed to allocate state\n");
92a33466e3SDmitry Baryshkov 		error = -ENOMEM;
93a33466e3SDmitry Baryshkov 		goto fail1;
94a33466e3SDmitry Baryshkov 	}
9578a56aabSPhil Blundell 
96a33466e3SDmitry Baryshkov 	platform_set_drvdata(pdev, ddata);
9778a56aabSPhil Blundell 
9878a56aabSPhil Blundell 	input->name = pdev->name;
9978a56aabSPhil Blundell 	input->phys = "gpio-keys/input0";
100469ba4dfSDmitry Torokhov 	input->dev.parent = &pdev->dev;
10178a56aabSPhil Blundell 
10278a56aabSPhil Blundell 	input->id.bustype = BUS_HOST;
10378a56aabSPhil Blundell 	input->id.vendor = 0x0001;
10478a56aabSPhil Blundell 	input->id.product = 0x0001;
10578a56aabSPhil Blundell 	input->id.version = 0x0100;
10678a56aabSPhil Blundell 
107b67b4b11SDominic Curran 	/* Enable auto repeat feature of Linux input subsystem */
108b67b4b11SDominic Curran 	if (pdata->rep)
109b67b4b11SDominic Curran 		__set_bit(EV_REP, input->evbit);
110b67b4b11SDominic Curran 
111a33466e3SDmitry Baryshkov 	ddata->input = input;
112a33466e3SDmitry Baryshkov 
11378a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
11484767d00SRoman Moravcik 		struct gpio_keys_button *button = &pdata->buttons[i];
115a33466e3SDmitry Baryshkov 		struct gpio_button_data *bdata = &ddata->data[i];
1166a2e3911SHerbert Valerio Riedel 		int irq;
11784767d00SRoman Moravcik 		unsigned int type = button->type ?: EV_KEY;
11878a56aabSPhil Blundell 
119a33466e3SDmitry Baryshkov 		bdata->input = input;
12074dd4393SUwe Kleine-König 		bdata->button = button;
121ca865a77SJani Nikula 		setup_timer(&bdata->timer,
122da0d03feSJani Nikula 			    gpio_keys_timer, (unsigned long)bdata);
123da0d03feSJani Nikula 		INIT_WORK(&bdata->work, gpio_keys_report_event);
124a33466e3SDmitry Baryshkov 
1256a2e3911SHerbert Valerio Riedel 		error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
1266a2e3911SHerbert Valerio Riedel 		if (error < 0) {
127*db19fd8bSBen Dooks 			dev_err(dev, "failed to request GPIO %d, error %d\n",
128*db19fd8bSBen Dooks 				button->gpio, error);
129a33466e3SDmitry Baryshkov 			goto fail2;
1306a2e3911SHerbert Valerio Riedel 		}
1316a2e3911SHerbert Valerio Riedel 
1326a2e3911SHerbert Valerio Riedel 		error = gpio_direction_input(button->gpio);
1336a2e3911SHerbert Valerio Riedel 		if (error < 0) {
134*db19fd8bSBen Dooks 			dev_err(dev, "failed to configure"
1356a2e3911SHerbert Valerio Riedel 				" direction for GPIO %d, error %d\n",
1366a2e3911SHerbert Valerio Riedel 				button->gpio, error);
1376a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
138a33466e3SDmitry Baryshkov 			goto fail2;
1396a2e3911SHerbert Valerio Riedel 		}
1406a2e3911SHerbert Valerio Riedel 
1416a2e3911SHerbert Valerio Riedel 		irq = gpio_to_irq(button->gpio);
142006df302SAnti Sullin 		if (irq < 0) {
143006df302SAnti Sullin 			error = irq;
144*db19fd8bSBen Dooks 			dev_err(dev, "Unable to get irq number "
1456a2e3911SHerbert Valerio Riedel 				"for GPIO %d, error %d\n",
146006df302SAnti Sullin 				button->gpio, error);
1476a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
148a33466e3SDmitry Baryshkov 			goto fail2;
149006df302SAnti Sullin 		}
150006df302SAnti Sullin 
151006df302SAnti Sullin 		error = request_irq(irq, gpio_keys_isr,
152558a5e29SDmitry Eremin-Solenikov 				    IRQF_SHARED |
15364e8563cSDmitry Torokhov 				    IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
15484767d00SRoman Moravcik 				    button->desc ? button->desc : "gpio_keys",
15557ffe9d5SUwe Kleine-König 				    bdata);
15678a56aabSPhil Blundell 		if (error) {
157*db19fd8bSBen Dooks 			dev_err(dev, "Unable to claim irq %d; error %d\n",
1580d98f6bbSPhilipp Zabel 				irq, error);
1596a2e3911SHerbert Valerio Riedel 			gpio_free(button->gpio);
160a33466e3SDmitry Baryshkov 			goto fail2;
16178a56aabSPhil Blundell 		}
16284767d00SRoman Moravcik 
163e15b0213SAnti Sullin 		if (button->wakeup)
164e15b0213SAnti Sullin 			wakeup = 1;
165e15b0213SAnti Sullin 
16684767d00SRoman Moravcik 		input_set_capability(input, type, button->code);
16778a56aabSPhil Blundell 	}
16878a56aabSPhil Blundell 
16978a56aabSPhil Blundell 	error = input_register_device(input);
17078a56aabSPhil Blundell 	if (error) {
171*db19fd8bSBen Dooks 		dev_err(dev, "Unable to register input device, "
172006df302SAnti Sullin 			"error: %d\n", error);
173a33466e3SDmitry Baryshkov 		goto fail2;
17478a56aabSPhil Blundell 	}
17578a56aabSPhil Blundell 
176e15b0213SAnti Sullin 	device_init_wakeup(&pdev->dev, wakeup);
177e15b0213SAnti Sullin 
17878a56aabSPhil Blundell 	return 0;
17978a56aabSPhil Blundell 
180a33466e3SDmitry Baryshkov  fail2:
1816a2e3911SHerbert Valerio Riedel 	while (--i >= 0) {
18257ffe9d5SUwe Kleine-König 		free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
183ca865a77SJani Nikula 		if (pdata->buttons[i].debounce_interval)
184ca865a77SJani Nikula 			del_timer_sync(&ddata->data[i].timer);
185da0d03feSJani Nikula 		cancel_work_sync(&ddata->data[i].work);
1866a2e3911SHerbert Valerio Riedel 		gpio_free(pdata->buttons[i].gpio);
1876a2e3911SHerbert Valerio Riedel 	}
18878a56aabSPhil Blundell 
189006df302SAnti Sullin 	platform_set_drvdata(pdev, NULL);
190a33466e3SDmitry Baryshkov  fail1:
19178a56aabSPhil Blundell 	input_free_device(input);
192a33466e3SDmitry Baryshkov 	kfree(ddata);
19378a56aabSPhil Blundell 
19478a56aabSPhil Blundell 	return error;
19578a56aabSPhil Blundell }
19678a56aabSPhil Blundell 
19778a56aabSPhil Blundell static int __devexit gpio_keys_remove(struct platform_device *pdev)
19878a56aabSPhil Blundell {
19978a56aabSPhil Blundell 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
200a33466e3SDmitry Baryshkov 	struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
201a33466e3SDmitry Baryshkov 	struct input_dev *input = ddata->input;
20278a56aabSPhil Blundell 	int i;
20378a56aabSPhil Blundell 
204e15b0213SAnti Sullin 	device_init_wakeup(&pdev->dev, 0);
205e15b0213SAnti Sullin 
20678a56aabSPhil Blundell 	for (i = 0; i < pdata->nbuttons; i++) {
2070d98f6bbSPhilipp Zabel 		int irq = gpio_to_irq(pdata->buttons[i].gpio);
20857ffe9d5SUwe Kleine-König 		free_irq(irq, &ddata->data[i]);
209ca865a77SJani Nikula 		if (pdata->buttons[i].debounce_interval)
210ca865a77SJani Nikula 			del_timer_sync(&ddata->data[i].timer);
211da0d03feSJani Nikula 		cancel_work_sync(&ddata->data[i].work);
2126a2e3911SHerbert Valerio Riedel 		gpio_free(pdata->buttons[i].gpio);
21378a56aabSPhil Blundell 	}
21478a56aabSPhil Blundell 
21578a56aabSPhil Blundell 	input_unregister_device(input);
21678a56aabSPhil Blundell 
21778a56aabSPhil Blundell 	return 0;
21878a56aabSPhil Blundell }
21978a56aabSPhil Blundell 
220e15b0213SAnti Sullin 
221e15b0213SAnti Sullin #ifdef CONFIG_PM
222ae78e0e0SMike Rapoport static int gpio_keys_suspend(struct device *dev)
223e15b0213SAnti Sullin {
224ae78e0e0SMike Rapoport 	struct platform_device *pdev = to_platform_device(dev);
225e15b0213SAnti Sullin 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
226e15b0213SAnti Sullin 	int i;
227e15b0213SAnti Sullin 
228e15b0213SAnti Sullin 	if (device_may_wakeup(&pdev->dev)) {
229e15b0213SAnti Sullin 		for (i = 0; i < pdata->nbuttons; i++) {
230e15b0213SAnti Sullin 			struct gpio_keys_button *button = &pdata->buttons[i];
231e15b0213SAnti Sullin 			if (button->wakeup) {
232e15b0213SAnti Sullin 				int irq = gpio_to_irq(button->gpio);
233e15b0213SAnti Sullin 				enable_irq_wake(irq);
234e15b0213SAnti Sullin 			}
235e15b0213SAnti Sullin 		}
236e15b0213SAnti Sullin 	}
237e15b0213SAnti Sullin 
238e15b0213SAnti Sullin 	return 0;
239e15b0213SAnti Sullin }
240e15b0213SAnti Sullin 
241ae78e0e0SMike Rapoport static int gpio_keys_resume(struct device *dev)
242e15b0213SAnti Sullin {
243ae78e0e0SMike Rapoport 	struct platform_device *pdev = to_platform_device(dev);
244e15b0213SAnti Sullin 	struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
245e15b0213SAnti Sullin 	int i;
246e15b0213SAnti Sullin 
247e15b0213SAnti Sullin 	if (device_may_wakeup(&pdev->dev)) {
248e15b0213SAnti Sullin 		for (i = 0; i < pdata->nbuttons; i++) {
249e15b0213SAnti Sullin 			struct gpio_keys_button *button = &pdata->buttons[i];
250e15b0213SAnti Sullin 			if (button->wakeup) {
251e15b0213SAnti Sullin 				int irq = gpio_to_irq(button->gpio);
252e15b0213SAnti Sullin 				disable_irq_wake(irq);
253e15b0213SAnti Sullin 			}
254e15b0213SAnti Sullin 		}
255e15b0213SAnti Sullin 	}
256e15b0213SAnti Sullin 
257e15b0213SAnti Sullin 	return 0;
258e15b0213SAnti Sullin }
259ae78e0e0SMike Rapoport 
260ae78e0e0SMike Rapoport static const struct dev_pm_ops gpio_keys_pm_ops = {
261ae78e0e0SMike Rapoport 	.suspend	= gpio_keys_suspend,
262ae78e0e0SMike Rapoport 	.resume		= gpio_keys_resume,
263ae78e0e0SMike Rapoport };
264e15b0213SAnti Sullin #endif
265e15b0213SAnti Sullin 
2669b07044cSUwe Kleine-König static struct platform_driver gpio_keys_device_driver = {
26778a56aabSPhil Blundell 	.probe		= gpio_keys_probe,
26878a56aabSPhil Blundell 	.remove		= __devexit_p(gpio_keys_remove),
26978a56aabSPhil Blundell 	.driver		= {
27078a56aabSPhil Blundell 		.name	= "gpio-keys",
271d7b5247bSKay Sievers 		.owner	= THIS_MODULE,
272ae78e0e0SMike Rapoport #ifdef CONFIG_PM
273ae78e0e0SMike Rapoport 		.pm	= &gpio_keys_pm_ops,
274ae78e0e0SMike Rapoport #endif
27578a56aabSPhil Blundell 	}
27678a56aabSPhil Blundell };
27778a56aabSPhil Blundell 
27878a56aabSPhil Blundell static int __init gpio_keys_init(void)
27978a56aabSPhil Blundell {
28078a56aabSPhil Blundell 	return platform_driver_register(&gpio_keys_device_driver);
28178a56aabSPhil Blundell }
28278a56aabSPhil Blundell 
28378a56aabSPhil Blundell static void __exit gpio_keys_exit(void)
28478a56aabSPhil Blundell {
28578a56aabSPhil Blundell 	platform_driver_unregister(&gpio_keys_device_driver);
28678a56aabSPhil Blundell }
28778a56aabSPhil Blundell 
28878a56aabSPhil Blundell module_init(gpio_keys_init);
28978a56aabSPhil Blundell module_exit(gpio_keys_exit);
29078a56aabSPhil Blundell 
29178a56aabSPhil Blundell MODULE_LICENSE("GPL");
29278a56aabSPhil Blundell MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
29378a56aabSPhil Blundell MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
294d7b5247bSKay Sievers MODULE_ALIAS("platform:gpio-keys");
295