xref: /openbmc/linux/drivers/input/misc/cobalt_btns.c (revision 5a0e3ad6)
1bebb8a2bSYoichi Yuasa /*
2bebb8a2bSYoichi Yuasa  *  Cobalt button interface driver.
3bebb8a2bSYoichi Yuasa  *
4ada8e951SYoichi Yuasa  *  Copyright (C) 2007-2008  Yoichi Yuasa <yuasa@linux-mips.org>
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>
255a0e3ad6STejun Heo #include <linux/slab.h>
26bebb8a2bSYoichi Yuasa 
27bebb8a2bSYoichi Yuasa #define BUTTONS_POLL_INTERVAL	30	/* msec */
28bebb8a2bSYoichi Yuasa #define BUTTONS_COUNT_THRESHOLD	3
29bebb8a2bSYoichi Yuasa #define BUTTONS_STATUS_MASK	0xfe000000
30bebb8a2bSYoichi Yuasa 
31b037b08eSDmitry Torokhov static const unsigned short cobalt_map[] = {
32b037b08eSDmitry Torokhov 	KEY_RESERVED,
33b037b08eSDmitry Torokhov 	KEY_RESTART,
34b037b08eSDmitry Torokhov 	KEY_LEFT,
35b037b08eSDmitry Torokhov 	KEY_UP,
36b037b08eSDmitry Torokhov 	KEY_DOWN,
37b037b08eSDmitry Torokhov 	KEY_RIGHT,
38b037b08eSDmitry Torokhov 	KEY_ENTER,
39b037b08eSDmitry Torokhov 	KEY_SELECT
40b037b08eSDmitry Torokhov };
41b037b08eSDmitry Torokhov 
42bebb8a2bSYoichi Yuasa struct buttons_dev {
433d29cdffSDmitry Torokhov 	struct input_polled_dev *poll_dev;
44b037b08eSDmitry Torokhov 	unsigned short keymap[ARRAY_SIZE(cobalt_map)];
45b037b08eSDmitry Torokhov 	int count[ARRAY_SIZE(cobalt_map)];
46bebb8a2bSYoichi Yuasa 	void __iomem *reg;
47bebb8a2bSYoichi Yuasa };
48bebb8a2bSYoichi Yuasa 
493d29cdffSDmitry Torokhov static void handle_buttons(struct input_polled_dev *dev)
50bebb8a2bSYoichi Yuasa {
513d29cdffSDmitry Torokhov 	struct buttons_dev *bdev = dev->private;
523d29cdffSDmitry Torokhov 	struct input_dev *input = dev->input;
53bebb8a2bSYoichi Yuasa 	uint32_t status;
54bebb8a2bSYoichi Yuasa 	int i;
55bebb8a2bSYoichi Yuasa 
56b037b08eSDmitry Torokhov 	status = ~readl(bdev->reg) >> 24;
57bebb8a2bSYoichi Yuasa 
58b037b08eSDmitry Torokhov 	for (i = 0; i < ARRAY_SIZE(bdev->keymap); i++) {
593c514387SYoichi Yuasa 		if (status & (1U << i)) {
60b037b08eSDmitry Torokhov 			if (++bdev->count[i] == BUTTONS_COUNT_THRESHOLD) {
61b037b08eSDmitry Torokhov 				input_event(input, EV_MSC, MSC_SCAN, i);
62b037b08eSDmitry Torokhov 				input_report_key(input, bdev->keymap[i], 1);
63b037b08eSDmitry Torokhov 				input_sync(input);
64b037b08eSDmitry Torokhov 			}
65bebb8a2bSYoichi Yuasa 		} else {
66b037b08eSDmitry Torokhov 			if (bdev->count[i] >= BUTTONS_COUNT_THRESHOLD) {
67b037b08eSDmitry Torokhov 				input_event(input, EV_MSC, MSC_SCAN, i);
68b037b08eSDmitry Torokhov 				input_report_key(input, bdev->keymap[i], 0);
693d29cdffSDmitry Torokhov 				input_sync(input);
70bebb8a2bSYoichi Yuasa 			}
71b037b08eSDmitry Torokhov 			bdev->count[i] = 0;
72bebb8a2bSYoichi Yuasa 		}
73bebb8a2bSYoichi Yuasa 	}
74bebb8a2bSYoichi Yuasa }
75bebb8a2bSYoichi Yuasa 
76bebb8a2bSYoichi Yuasa static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
77bebb8a2bSYoichi Yuasa {
78bebb8a2bSYoichi Yuasa 	struct buttons_dev *bdev;
793d29cdffSDmitry Torokhov 	struct input_polled_dev *poll_dev;
80bebb8a2bSYoichi Yuasa 	struct input_dev *input;
81bebb8a2bSYoichi Yuasa 	struct resource *res;
82bebb8a2bSYoichi Yuasa 	int error, i;
83bebb8a2bSYoichi Yuasa 
84bebb8a2bSYoichi Yuasa 	bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
853d29cdffSDmitry Torokhov 	poll_dev = input_allocate_polled_device();
863d29cdffSDmitry Torokhov 	if (!bdev || !poll_dev) {
87bebb8a2bSYoichi Yuasa 		error = -ENOMEM;
88bebb8a2bSYoichi Yuasa 		goto err_free_mem;
89bebb8a2bSYoichi Yuasa 	}
90bebb8a2bSYoichi Yuasa 
91b037b08eSDmitry Torokhov 	memcpy(bdev->keymap, cobalt_map, sizeof(bdev->keymap));
92b037b08eSDmitry Torokhov 
933d29cdffSDmitry Torokhov 	poll_dev->private = bdev;
943d29cdffSDmitry Torokhov 	poll_dev->poll = handle_buttons;
953d29cdffSDmitry Torokhov 	poll_dev->poll_interval = BUTTONS_POLL_INTERVAL;
963d29cdffSDmitry Torokhov 
973d29cdffSDmitry Torokhov 	input = poll_dev->input;
98bebb8a2bSYoichi Yuasa 	input->name = "Cobalt buttons";
99bebb8a2bSYoichi Yuasa 	input->phys = "cobalt/input0";
100bebb8a2bSYoichi Yuasa 	input->id.bustype = BUS_HOST;
1013c514387SYoichi Yuasa 	input->dev.parent = &pdev->dev;
102bebb8a2bSYoichi Yuasa 
1033c514387SYoichi Yuasa 	input->keycode = bdev->keymap;
1043c514387SYoichi Yuasa 	input->keycodemax = ARRAY_SIZE(bdev->keymap);
105b037b08eSDmitry Torokhov 	input->keycodesize = sizeof(unsigned short);
106b037b08eSDmitry Torokhov 
107b037b08eSDmitry Torokhov 	input_set_capability(input, EV_MSC, MSC_SCAN);
108b037b08eSDmitry Torokhov 	__set_bit(EV_KEY, input->evbit);
1093c514387SYoichi Yuasa 	for (i = 0; i < ARRAY_SIZE(cobalt_map); i++)
1103c514387SYoichi Yuasa 		__set_bit(bdev->keymap[i], input->keybit);
111b037b08eSDmitry Torokhov 	__clear_bit(KEY_RESERVED, input->keybit);
112bebb8a2bSYoichi Yuasa 
113bebb8a2bSYoichi Yuasa 	res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
114bebb8a2bSYoichi Yuasa 	if (!res) {
115bebb8a2bSYoichi Yuasa 		error = -EBUSY;
116bebb8a2bSYoichi Yuasa 		goto err_free_mem;
117bebb8a2bSYoichi Yuasa 	}
118bebb8a2bSYoichi Yuasa 
1193d29cdffSDmitry Torokhov 	bdev->poll_dev = poll_dev;
12072398e4bSJulia Lawall 	bdev->reg = ioremap(res->start, resource_size(res));
121bebb8a2bSYoichi Yuasa 	dev_set_drvdata(&pdev->dev, bdev);
122bebb8a2bSYoichi Yuasa 
1233d29cdffSDmitry Torokhov 	error = input_register_polled_device(poll_dev);
124bebb8a2bSYoichi Yuasa 	if (error)
125bebb8a2bSYoichi Yuasa 		goto err_iounmap;
126bebb8a2bSYoichi Yuasa 
127bebb8a2bSYoichi Yuasa 	return 0;
128bebb8a2bSYoichi Yuasa 
129bebb8a2bSYoichi Yuasa  err_iounmap:
130bebb8a2bSYoichi Yuasa 	iounmap(bdev->reg);
131bebb8a2bSYoichi Yuasa  err_free_mem:
1323d29cdffSDmitry Torokhov 	input_free_polled_device(poll_dev);
133bebb8a2bSYoichi Yuasa 	kfree(bdev);
134bebb8a2bSYoichi Yuasa 	dev_set_drvdata(&pdev->dev, NULL);
135bebb8a2bSYoichi Yuasa 	return error;
136bebb8a2bSYoichi Yuasa }
137bebb8a2bSYoichi Yuasa 
138bebb8a2bSYoichi Yuasa static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
139bebb8a2bSYoichi Yuasa {
140bebb8a2bSYoichi Yuasa 	struct device *dev = &pdev->dev;
141bebb8a2bSYoichi Yuasa 	struct buttons_dev *bdev = dev_get_drvdata(dev);
142bebb8a2bSYoichi Yuasa 
1433d29cdffSDmitry Torokhov 	input_unregister_polled_device(bdev->poll_dev);
1443d29cdffSDmitry Torokhov 	input_free_polled_device(bdev->poll_dev);
145bebb8a2bSYoichi Yuasa 	iounmap(bdev->reg);
146bebb8a2bSYoichi Yuasa 	kfree(bdev);
147bebb8a2bSYoichi Yuasa 	dev_set_drvdata(dev, NULL);
148bebb8a2bSYoichi Yuasa 
149bebb8a2bSYoichi Yuasa 	return 0;
150bebb8a2bSYoichi Yuasa }
151bebb8a2bSYoichi Yuasa 
152ada8e951SYoichi Yuasa MODULE_AUTHOR("Yoichi Yuasa <yuasa@linux-mips.org>");
15326135ed3SMartin Michlmayr MODULE_DESCRIPTION("Cobalt button interface driver");
15426135ed3SMartin Michlmayr MODULE_LICENSE("GPL");
155d7b5247bSKay Sievers /* work with hotplug and coldplug */
156d7b5247bSKay Sievers MODULE_ALIAS("platform:Cobalt buttons");
157d7b5247bSKay Sievers 
158bebb8a2bSYoichi Yuasa static struct platform_driver cobalt_buttons_driver = {
159bebb8a2bSYoichi Yuasa 	.probe	= cobalt_buttons_probe,
160bebb8a2bSYoichi Yuasa 	.remove	= __devexit_p(cobalt_buttons_remove),
161bebb8a2bSYoichi Yuasa 	.driver	= {
162bebb8a2bSYoichi Yuasa 		.name	= "Cobalt buttons",
163bebb8a2bSYoichi Yuasa 		.owner	= THIS_MODULE,
164bebb8a2bSYoichi Yuasa 	},
165bebb8a2bSYoichi Yuasa };
166bebb8a2bSYoichi Yuasa 
167bebb8a2bSYoichi Yuasa static int __init cobalt_buttons_init(void)
168bebb8a2bSYoichi Yuasa {
169d0a0515fSDmitry Torokhov 	return platform_driver_register(&cobalt_buttons_driver);
170bebb8a2bSYoichi Yuasa }
171bebb8a2bSYoichi Yuasa 
172bebb8a2bSYoichi Yuasa static void __exit cobalt_buttons_exit(void)
173bebb8a2bSYoichi Yuasa {
174bebb8a2bSYoichi Yuasa 	platform_driver_unregister(&cobalt_buttons_driver);
175bebb8a2bSYoichi Yuasa }
176bebb8a2bSYoichi Yuasa 
177bebb8a2bSYoichi Yuasa module_init(cobalt_buttons_init);
178bebb8a2bSYoichi Yuasa module_exit(cobalt_buttons_exit);
179