]> err.no Git - linux-2.6/blob - drivers/input/misc/cobalt_btns.c
Input: add driver for MIPS Cobalt back panel buttons
[linux-2.6] / drivers / input / misc / cobalt_btns.c
1 /*
2  *  Cobalt button interface driver.
3  *
4  *  Copyright (C) 2007  Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
5  *
6  *  This program is free software; you can redistribute it and/or modify
7  *  it under the terms of the GNU General Public License as published by
8  *  the Free Software Foundation; either version 2 of the License, or
9  *  (at your option) any later version.
10  *
11  *  This program is distributed in the hope that it will be useful,
12  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  *  GNU General Public License for more details.
15  *
16  *  You should have received a copy of the GNU General Public License
17  *  along with this program; if not, write to the Free Software
18  *  Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301  USA
19  */
20 #include <linux/init.h>
21 #include <linux/input.h>
22 #include <linux/ioport.h>
23 #include <linux/module.h>
24 #include <linux/platform_device.h>
25 #include <linux/jiffies.h>
26 #include <linux/timer.h>
27
28 #define BUTTONS_POLL_INTERVAL   30      /* msec */
29 #define BUTTONS_COUNT_THRESHOLD 3
30 #define BUTTONS_STATUS_MASK     0xfe000000
31
32 struct buttons_dev {
33         struct input_dev *input;
34         void __iomem *reg;
35 };
36
37 struct buttons_map {
38         uint32_t mask;
39         int keycode;
40         int count;
41 };
42
43 static struct buttons_map buttons_map[] = {
44         { 0x02000000, KEY_RESTART, },
45         { 0x04000000, KEY_LEFT, },
46         { 0x08000000, KEY_UP, },
47         { 0x10000000, KEY_DOWN, },
48         { 0x20000000, KEY_RIGHT, },
49         { 0x40000000, KEY_ENTER, },
50         { 0x80000000, KEY_SELECT, },
51 };
52
53 static struct resource cobalt_buttons_resource __initdata = {
54         .start  = 0x1d000000,
55         .end    = 0x1d000003,
56         .flags  = IORESOURCE_MEM,
57 };
58
59 static struct platform_device *cobalt_buttons_device;
60
61 static struct timer_list buttons_timer;
62
63 static void handle_buttons(unsigned long data)
64 {
65         struct buttons_map *button = buttons_map;
66         struct buttons_dev *bdev;
67         uint32_t status;
68         int i;
69
70         bdev = (struct buttons_dev *)data;
71         status = readl(bdev->reg);
72         status = ~status & BUTTONS_STATUS_MASK;
73
74         for (i = 0; i < ARRAY_SIZE(buttons_map); i++) {
75                 if (status & button->mask) {
76                         button->count++;
77                 } else {
78                         if (button->count >= BUTTONS_COUNT_THRESHOLD) {
79                                 input_report_key(bdev->input, button->keycode, 0);
80                                 input_sync(bdev->input);
81                         }
82                         button->count = 0;
83                 }
84
85                 if (button->count == BUTTONS_COUNT_THRESHOLD) {
86                         input_report_key(bdev->input, button->keycode, 1);
87                         input_sync(bdev->input);
88                 }
89
90                 button++;
91         }
92
93         mod_timer(&buttons_timer, jiffies + msecs_to_jiffies(BUTTONS_POLL_INTERVAL));
94 }
95
96 static int cobalt_buttons_open(struct input_dev *dev)
97 {
98         mod_timer(&buttons_timer, jiffies + msecs_to_jiffies(BUTTONS_POLL_INTERVAL));
99
100         return 0;
101 }
102
103 static void cobalt_buttons_close(struct input_dev *dev)
104 {
105         del_timer_sync(&buttons_timer);
106 }
107
108 static int __devinit cobalt_buttons_probe(struct platform_device *pdev)
109 {
110         struct buttons_dev *bdev;
111         struct input_dev *input;
112         struct resource *res;
113         int error, i;
114
115         bdev = kzalloc(sizeof(struct buttons_dev), GFP_KERNEL);
116         input = input_allocate_device();
117         if (!bdev || !input) {
118                 error = -ENOMEM;
119                 goto err_free_mem;
120         }
121
122         input->name = "Cobalt buttons";
123         input->phys = "cobalt/input0";
124         input->id.bustype = BUS_HOST;
125         input->cdev.dev = &pdev->dev;
126         input->open = cobalt_buttons_open;
127         input->close = cobalt_buttons_close;
128
129         input->evbit[0] = BIT(EV_KEY);
130         for (i = 0; i < ARRAY_SIZE(buttons_map); i++) {
131                 set_bit(buttons_map[i].keycode, input->keybit);
132                 buttons_map[i].count = 0;
133         }
134
135         res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
136         if (!res) {
137                 error = -EBUSY;
138                 goto err_free_mem;
139         }
140
141         bdev->input = input;
142         bdev->reg = ioremap(res->start, res->end - res->start + 1);
143         dev_set_drvdata(&pdev->dev, bdev);
144
145         setup_timer(&buttons_timer, handle_buttons, (unsigned long)bdev);
146
147         error = input_register_device(input);
148         if (error)
149                 goto err_iounmap;
150
151         return 0;
152
153  err_iounmap:
154         iounmap(bdev->reg);
155  err_free_mem:
156         input_free_device(input);
157         kfree(bdev);
158         dev_set_drvdata(&pdev->dev, NULL);
159         return error;
160 }
161
162 static int __devexit cobalt_buttons_remove(struct platform_device *pdev)
163 {
164         struct device *dev = &pdev->dev;
165         struct buttons_dev *bdev = dev_get_drvdata(dev);
166
167         input_unregister_device(bdev->input);
168         iounmap(bdev->reg);
169         kfree(bdev);
170         dev_set_drvdata(dev, NULL);
171
172         return 0;
173 }
174
175 static struct platform_driver cobalt_buttons_driver = {
176         .probe  = cobalt_buttons_probe,
177         .remove = __devexit_p(cobalt_buttons_remove),
178         .driver = {
179                 .name   = "Cobalt buttons",
180                 .owner  = THIS_MODULE,
181         },
182 };
183
184 static int __init cobalt_buttons_init(void)
185 {
186         int retval;
187
188         cobalt_buttons_device = platform_device_register_simple("Cobalt buttons", -1,
189                                                                 &cobalt_buttons_resource, 1);
190         if (IS_ERR(cobalt_buttons_device)) {
191                 retval = PTR_ERR(cobalt_buttons_device);
192                 return retval;
193         }
194
195         retval = platform_driver_register(&cobalt_buttons_driver);
196         if (retval < 0)
197                 platform_device_unregister(cobalt_buttons_device);
198
199         return retval;
200 }
201
202 static void __exit cobalt_buttons_exit(void)
203 {
204         platform_driver_unregister(&cobalt_buttons_driver);
205         platform_device_unregister(cobalt_buttons_device);
206 }
207
208 module_init(cobalt_buttons_init);
209 module_exit(cobalt_buttons_exit);