xref: /openbmc/linux/drivers/input/misc/cobalt_btns.c (revision 26135ed3)
1bebb8a2bSYoichi Yuasa /*
2bebb8a2bSYoichi Yuasa  *  Cobalt button interface driver.
3bebb8a2bSYoichi Yuasa  *
43c514387SYoichi Yuasa  *  Copyright (C) 2007-2008  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
5bebb8a2bSYoichi Yuasa  *
6bebb8a2bSYoichi Yuasa  *  This program is free software; you can redistribute it and/or modify
7bebb8a2bSYoichi Yuasa  *  it under the terms of the GNU General Public License as published by
8bebb8a2bSYoichi Yuasa  *  the Free Software Foundation; either version 2 of the License, or
9bebb8a2bSYoichi Yuasa  *  (at your option) any later version.
10bebb8a2bSYoichi Yuasa  *
11bebb8a2bSYoichi Yuasa  *  This program is distributed in the hope that it will be useful,
12bebb8a2bSYoichi Yuasa  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13bebb8a2bSYoichi Yuasa  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14bebb8a2bSYoichi Yuasa  *  GNU General Public License for more details.
15bebb8a2bSYoichi Yuasa  *
16bebb8a2bSYoichi Yuasa  *  You should have received a copy of the GNU General Public License
17bebb8a2bSYoichi Yuasa  *  along with this program; if not, write to the Free Software
18bebb8a2bSYoichi Yuasa  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19bebb8a2bSYoichi Yuasa  */
20bebb8a2bSYoichi Yuasa #include <linux/init.h>
213d29cdffSDmitry Torokhov #include <linux/input-polldev.h>
22bebb8a2bSYoichi Yuasa #include <linux/ioport.h>
23bebb8a2bSYoichi Yuasa #include <linux/module.h>
24bebb8a2bSYoichi Yuasa #include <linux/platform_device.h>
25bebb8a2bSYoichi Yuasa 
26bebb8a2bSYoichi Yuasa #define BUTTONS_POLL_INTERVAL	30	/* msec */
27bebb8a2bSYoichi Yuasa #define BUTTONS_COUNT_THRESHOLD	3
28bebb8a2bSYoichi Yuasa #define BUTTONS_STATUS_MASK	0xfe000000
29bebb8a2bSYoichi Yuasa 
30b037b08eSDmitry Torokhov static const unsigned short cobalt_map[] = {
31b037b08eSDmitry Torokhov 	KEY_RESERVED,
32b037b08eSDmitry Torokhov 	KEY_RESTART,
33b037b08eSDmitry Torokhov 	KEY_LEFT,
34b037b08eSDmitry Torokhov 	KEY_UP,
35b037b08eSDmitry Torokhov 	KEY_DOWN,
36b037b08eSDmitry Torokhov 	KEY_RIGHT,
37b037b08eSDmitry Torokhov 	KEY_ENTER,
38b037b08eSDmitry Torokhov 	KEY_SELECT
39b037b08eSDmitry Torokhov };
40b037b08eSDmitry Torokhov 
41bebb8a2bSYoichi Yuasa struct buttons_dev {
423d29cdffSDmitry Torokhov 	struct input_polled_dev *poll_dev;
43b037b08eSDmitry Torokhov 	unsigned short keymap[ARRAY_SIZE(cobalt_map)];
44b037b08eSDmitry Torokhov 	int count[ARRAY_SIZE(cobalt_map)];
45bebb8a2bSYoichi Yuasa 	void __iomem *reg;
46bebb8a2bSYoichi Yuasa };
47bebb8a2bSYoichi Yuasa 
483d29cdffSDmitry Torokhov static void handle_buttons(struct input_polled_dev *dev)
49bebb8a2bSYoichi Yuasa {
503d29cdffSDmitry Torokhov 	struct buttons_dev *bdev = dev->private;
513d29cdffSDmitry Torokhov 	struct input_dev *input = dev->input;
52bebb8a2bSYoichi Yuasa 	uint32_t status;
53bebb8a2bSYoichi Yuasa 	int i;
54bebb8a2bSYoichi Yuasa 
55b037b08eSDmitry Torokhov 	status = ~readl(bdev->reg) >> 24;
56bebb8a2bSYoichi Yuasa 
57b037b08eSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
583c514387SYoichi Yuasa 		if (status & (1U << i)) {
59b037b08eSDmitry Torokhov 			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
60b037b08eSDmitry Torokhov 				input_event(input, EV_MSC, MSC_SCAN, i);
61b037b08eSDmitry Torokhov 				input_report_key(input, bdev->keymap[i], 1);
62b037b08eSDmitry Torokhov 				input_sync(input);
63b037b08eSDmitry Torokhov 			}
64bebb8a2bSYoichi Yuasa 		} else {
65b037b08eSDmitry Torokhov 			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
66b037b08eSDmitry Torokhov 				input_event(input, EV_MSC, MSC_SCAN, i);
67b037b08eSDmitry Torokhov 				input_report_key(input, bdev->keymap[i], 0);
683d29cdffSDmitry Torokhov 				input_sync(input);
69bebb8a2bSYoichi Yuasa 			}
70b037b08eSDmitry Torokhov 			bdev->count[i] = 0;
71bebb8a2bSYoichi Yuasa 		}
72bebb8a2bSYoichi Yuasa 	}
73bebb8a2bSYoichi Yuasa }
74bebb8a2bSYoichi Yuasa 
75bebb8a2bSYoichi Yuasa static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
76bebb8a2bSYoichi Yuasa {
77bebb8a2bSYoichi Yuasa 	struct buttons_dev *bdev;
783d29cdffSDmitry Torokhov 	struct input_polled_dev *poll_dev;
79bebb8a2bSYoichi Yuasa 	struct input_dev *input;
80bebb8a2bSYoichi Yuasa 	struct resource *res;
81bebb8a2bSYoichi Yuasa 	int error, i;
82bebb8a2bSYoichi Yuasa 
83bebb8a2bSYoichi Yuasa 	bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
843d29cdffSDmitry Torokhov 	poll_dev = input_allocate_polled_device();
853d29cdffSDmitry Torokhov 	if (!bdev || !poll_dev) {
86bebb8a2bSYoichi Yuasa 		error = -ENOMEM;
87bebb8a2bSYoichi Yuasa 		goto err_free_mem;
88bebb8a2bSYoichi Yuasa 	}
89bebb8a2bSYoichi Yuasa 
90b037b08eSDmitry Torokhov 	memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
91b037b08eSDmitry Torokhov 
923d29cdffSDmitry Torokhov 	poll_dev->private = bdev;
933d29cdffSDmitry Torokhov 	poll_dev->poll = handle_buttons;
943d29cdffSDmitry Torokhov 	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
953d29cdffSDmitry Torokhov 
963d29cdffSDmitry Torokhov 	input = poll_dev->input;
97bebb8a2bSYoichi Yuasa 	input->name = "Cobalt buttons";
98bebb8a2bSYoichi Yuasa 	input->phys = "cobalt/input0";
99bebb8a2bSYoichi Yuasa 	input->id.bustype = BUS_HOST;
1003c514387SYoichi Yuasa 	input->dev.parent = &pdev->dev;
101bebb8a2bSYoichi Yuasa 
1023c514387SYoichi Yuasa 	input->keycode = bdev->keymap;
1033c514387SYoichi Yuasa 	input->keycodemax = ARRAY_SIZE(bdev->keymap);
104b037b08eSDmitry Torokhov 	input->keycodesize = sizeof(unsigned short);
105b037b08eSDmitry Torokhov 
106b037b08eSDmitry Torokhov 	input_set_capability(input, EV_MSC, MSC_SCAN);
107b037b08eSDmitry Torokhov 	__set_bit(EV_KEY, input->evbit);
1083c514387SYoichi Yuasa 	for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
1093c514387SYoichi Yuasa 		__set_bit(bdev->keymap[i], input->keybit);
110b037b08eSDmitry Torokhov 	__clear_bit(KEY_RESERVED, input->keybit);
111bebb8a2bSYoichi Yuasa 
112bebb8a2bSYoichi Yuasa 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
113bebb8a2bSYoichi Yuasa 	if (!res) {
114bebb8a2bSYoichi Yuasa 		error = -EBUSY;
115bebb8a2bSYoichi Yuasa 		goto err_free_mem;
116bebb8a2bSYoichi Yuasa 	}
117bebb8a2bSYoichi Yuasa 
1183d29cdffSDmitry Torokhov 	bdev->poll_dev = poll_dev;
119bebb8a2bSYoichi Yuasa 	bdev->reg = ioremap(res->start, res->end - res->start + 1);
120bebb8a2bSYoichi Yuasa 	dev_set_drvdata(&pdev->dev, bdev);
121bebb8a2bSYoichi Yuasa 
1223d29cdffSDmitry Torokhov 	error = input_register_polled_device(poll_dev);
123bebb8a2bSYoichi Yuasa 	if (error)
124bebb8a2bSYoichi Yuasa 		goto err_iounmap;
125bebb8a2bSYoichi Yuasa 
126bebb8a2bSYoichi Yuasa 	return 0;
127bebb8a2bSYoichi Yuasa 
128bebb8a2bSYoichi Yuasa  err_iounmap:
129bebb8a2bSYoichi Yuasa 	iounmap(bdev->reg);
130bebb8a2bSYoichi Yuasa  err_free_mem:
1313d29cdffSDmitry Torokhov 	input_free_polled_device(poll_dev);
132bebb8a2bSYoichi Yuasa 	kfree(bdev);
133bebb8a2bSYoichi Yuasa 	dev_set_drvdata(&pdev->dev, NULL);
134bebb8a2bSYoichi Yuasa 	return error;
135bebb8a2bSYoichi Yuasa }
136bebb8a2bSYoichi Yuasa 
137bebb8a2bSYoichi Yuasa static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
138bebb8a2bSYoichi Yuasa {
139bebb8a2bSYoichi Yuasa 	struct device *dev = &pdev->dev;
140bebb8a2bSYoichi Yuasa 	struct buttons_dev *bdev = dev_get_drvdata(dev);
141bebb8a2bSYoichi Yuasa 
1423d29cdffSDmitry Torokhov 	input_unregister_polled_device(bdev->poll_dev);
1433d29cdffSDmitry Torokhov 	input_free_polled_device(bdev->poll_dev);
144bebb8a2bSYoichi Yuasa 	iounmap(bdev->reg);
145bebb8a2bSYoichi Yuasa 	kfree(bdev);
146bebb8a2bSYoichi Yuasa 	dev_set_drvdata(dev, NULL);
147bebb8a2bSYoichi Yuasa 
148bebb8a2bSYoichi Yuasa 	return 0;
149bebb8a2bSYoichi Yuasa }
150bebb8a2bSYoichi Yuasa 
15126135ed3SMartin Michlmayr MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");
15226135ed3SMartin Michlmayr MODULE_DESCRIPTION("Cobalt button interface driver");
15326135ed3SMartin Michlmayr MODULE_LICENSE("GPL");
154d7b5247bSKay Sievers /* work with hotplug and coldplug */
155d7b5247bSKay Sievers MODULE_ALIAS("platform:Cobalt buttons");
156d7b5247bSKay Sievers 
157bebb8a2bSYoichi Yuasa static struct platform_driver cobalt_buttons_driver = {
158bebb8a2bSYoichi Yuasa 	.probe	= cobalt_buttons_probe,
159bebb8a2bSYoichi Yuasa 	.remove	= __devexit_p(cobalt_buttons_remove),
160bebb8a2bSYoichi Yuasa 	.driver	= {
161bebb8a2bSYoichi Yuasa 		.name	= "Cobalt buttons",
162bebb8a2bSYoichi Yuasa 		.owner	= THIS_MODULE,
163bebb8a2bSYoichi Yuasa 	},
164bebb8a2bSYoichi Yuasa };
165bebb8a2bSYoichi Yuasa 
166bebb8a2bSYoichi Yuasa static int __init cobalt_buttons_init(void)
167bebb8a2bSYoichi Yuasa {
168d0a0515fSDmitry Torokhov 	return platform_driver_register(&cobalt_buttons_driver);
169bebb8a2bSYoichi Yuasa }
170bebb8a2bSYoichi Yuasa 
171bebb8a2bSYoichi Yuasa static void __exit cobalt_buttons_exit(void)
172bebb8a2bSYoichi Yuasa {
173bebb8a2bSYoichi Yuasa 	platform_driver_unregister(&cobalt_buttons_driver);
174bebb8a2bSYoichi Yuasa }
175bebb8a2bSYoichi Yuasa 
176bebb8a2bSYoichi Yuasa module_init(cobalt_buttons_init);
177bebb8a2bSYoichi Yuasa module_exit(cobalt_buttons_exit);
178