diff options
author | Michael Hennerich <michael.hennerich@analog.com> | 2009-07-23 00:51:34 -0400 |
---|---|---|
committer | Dmitry Torokhov <dmitry.torokhov@gmail.com> | 2009-07-23 04:15:09 -0400 |
commit | 4832958218f96f98009c5e01729fbe2b48c7124c (patch) | |
tree | 2bb83fbf9b9f2ef3d60d2a2e3e55fa1b42a1f847 /drivers/input/misc | |
parent | 52ec7752b457311f10f5a8d16faa8ac2e684eb65 (diff) |
Input: add Blackfin rotary input driver
This driver handles the Blackfin on-chip rotary peripheral.
Signed-off-by: Michael Hennerich <michael.hennerich@analog.com>
Signed-off-by: Bryan Wu <cooloney@kernel.org>
Signed-off-by: Mike Frysinger <vapier@gentoo.org>
Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers/input/misc')
-rw-r--r-- | drivers/input/misc/Kconfig | 10 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/bfin_rotary.c | 283 |
3 files changed, 294 insertions, 0 deletions
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 1acfa3a05aad..cbe21bc96b52 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig | |||
@@ -269,4 +269,14 @@ config INPUT_DM355EVM | |||
269 | 269 | ||
270 | To compile this driver as a module, choose M here: the | 270 | To compile this driver as a module, choose M here: the |
271 | module will be called dm355evm_keys. | 271 | module will be called dm355evm_keys. |
272 | |||
273 | config INPUT_BFIN_ROTARY | ||
274 | tristate "Blackfin Rotary support" | ||
275 | depends on BF54x || BF52x | ||
276 | help | ||
277 | Say Y here if you want to use the Blackfin Rotary. | ||
278 | |||
279 | To compile this driver as a module, choose M here: the | ||
280 | module will be called bfin-rotary. | ||
281 | |||
272 | endif | 282 | endif |
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index 0d979fd4cd57..79c1e9a5ea31 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile | |||
@@ -8,6 +8,7 @@ obj-$(CONFIG_INPUT_APANEL) += apanel.o | |||
8 | obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o | 8 | obj-$(CONFIG_INPUT_ATI_REMOTE) += ati_remote.o |
9 | obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o | 9 | obj-$(CONFIG_INPUT_ATI_REMOTE2) += ati_remote2.o |
10 | obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o | 10 | obj-$(CONFIG_INPUT_ATLAS_BTNS) += atlas_btns.o |
11 | obj-$(CONFIG_INPUT_BFIN_ROTARY) += bfin_rotary.o | ||
11 | obj-$(CONFIG_INPUT_CM109) += cm109.o | 12 | obj-$(CONFIG_INPUT_CM109) += cm109.o |
12 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o | 13 | obj-$(CONFIG_INPUT_COBALT_BTNS) += cobalt_btns.o |
13 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o | 14 | obj-$(CONFIG_INPUT_DM355EVM) += dm355evm_keys.o |
diff --git a/drivers/input/misc/bfin_rotary.c b/drivers/input/misc/bfin_rotary.c new file mode 100644 index 000000000000..690f3fafa03b --- /dev/null +++ b/drivers/input/misc/bfin_rotary.c | |||
@@ -0,0 +1,283 @@ | |||
1 | /* | ||
2 | * Rotary counter driver for Analog Devices Blackfin Processors | ||
3 | * | ||
4 | * Copyright 2008-2009 Analog Devices Inc. | ||
5 | * Licensed under the GPL-2 or later. | ||
6 | */ | ||
7 | |||
8 | #include <linux/module.h> | ||
9 | #include <linux/version.h> | ||
10 | #include <linux/init.h> | ||
11 | #include <linux/interrupt.h> | ||
12 | #include <linux/irq.h> | ||
13 | #include <linux/pm.h> | ||
14 | #include <linux/platform_device.h> | ||
15 | #include <linux/input.h> | ||
16 | |||
17 | #include <asm/portmux.h> | ||
18 | #include <asm/bfin_rotary.h> | ||
19 | |||
20 | static const u16 per_cnt[] = { | ||
21 | P_CNT_CUD, | ||
22 | P_CNT_CDG, | ||
23 | P_CNT_CZM, | ||
24 | 0 | ||
25 | }; | ||
26 | |||
27 | struct bfin_rot { | ||
28 | struct input_dev *input; | ||
29 | int irq; | ||
30 | unsigned int up_key; | ||
31 | unsigned int down_key; | ||
32 | unsigned int button_key; | ||
33 | unsigned int rel_code; | ||
34 | unsigned short cnt_config; | ||
35 | unsigned short cnt_imask; | ||
36 | unsigned short cnt_debounce; | ||
37 | }; | ||
38 | |||
39 | static void report_key_event(struct input_dev *input, int keycode) | ||
40 | { | ||
41 | /* simulate a press-n-release */ | ||
42 | input_report_key(input, keycode, 1); | ||
43 | input_sync(input); | ||
44 | input_report_key(input, keycode, 0); | ||
45 | input_sync(input); | ||
46 | } | ||
47 | |||
48 | static void report_rotary_event(struct bfin_rot *rotary, int delta) | ||
49 | { | ||
50 | struct input_dev *input = rotary->input; | ||
51 | |||
52 | if (rotary->up_key) { | ||
53 | report_key_event(input, | ||
54 | delta > 0 ? rotary->up_key : rotary->down_key); | ||
55 | } else { | ||
56 | input_report_rel(input, rotary->rel_code, delta); | ||
57 | input_sync(input); | ||
58 | } | ||
59 | } | ||
60 | |||
61 | static irqreturn_t bfin_rotary_isr(int irq, void *dev_id) | ||
62 | { | ||
63 | struct platform_device *pdev = dev_id; | ||
64 | struct bfin_rot *rotary = platform_get_drvdata(pdev); | ||
65 | int delta; | ||
66 | |||
67 | switch (bfin_read_CNT_STATUS()) { | ||
68 | |||
69 | case ICII: | ||
70 | break; | ||
71 | |||
72 | case UCII: | ||
73 | case DCII: | ||
74 | delta = bfin_read_CNT_COUNTER(); | ||
75 | if (delta) | ||
76 | report_rotary_event(rotary, delta); | ||
77 | break; | ||
78 | |||
79 | case CZMII: | ||
80 | report_key_event(rotary->input, rotary->button_key); | ||
81 | break; | ||
82 | |||
83 | default: | ||
84 | break; | ||
85 | } | ||
86 | |||
87 | bfin_write_CNT_COMMAND(W1LCNT_ZERO); /* Clear COUNTER */ | ||
88 | bfin_write_CNT_STATUS(-1); /* Clear STATUS */ | ||
89 | |||
90 | return IRQ_HANDLED; | ||
91 | } | ||
92 | |||
93 | static int __devinit bfin_rotary_probe(struct platform_device *pdev) | ||
94 | { | ||
95 | struct bfin_rotary_platform_data *pdata = pdev->dev.platform_data; | ||
96 | struct bfin_rot *rotary; | ||
97 | struct input_dev *input; | ||
98 | int error; | ||
99 | |||
100 | /* Basic validation */ | ||
101 | if ((pdata->rotary_up_key && !pdata->rotary_down_key) || | ||
102 | (!pdata->rotary_up_key && pdata->rotary_down_key)) { | ||
103 | return -EINVAL; | ||
104 | } | ||
105 | |||
106 | error = peripheral_request_list(per_cnt, dev_name(&pdev->dev)); | ||
107 | if (error) { | ||
108 | dev_err(&pdev->dev, "requesting peripherals failed\n"); | ||
109 | return error; | ||
110 | } | ||
111 | |||
112 | rotary = kzalloc(sizeof(struct bfin_rot), GFP_KERNEL); | ||
113 | input = input_allocate_device(); | ||
114 | if (!rotary || !input) { | ||
115 | error = -ENOMEM; | ||
116 | goto out1; | ||
117 | } | ||
118 | |||
119 | rotary->input = input; | ||
120 | |||
121 | rotary->up_key = pdata->rotary_up_key; | ||
122 | rotary->down_key = pdata->rotary_down_key; | ||
123 | rotary->button_key = pdata->rotary_button_key; | ||
124 | rotary->rel_code = pdata->rotary_rel_code; | ||
125 | |||
126 | error = rotary->irq = platform_get_irq(pdev, 0); | ||
127 | if (error < 0) | ||
128 | goto out1; | ||
129 | |||
130 | input->name = pdev->name; | ||
131 | input->phys = "bfin-rotary/input0"; | ||
132 | input->dev.parent = &pdev->dev; | ||
133 | |||
134 | input_set_drvdata(input, rotary); | ||
135 | |||
136 | input->id.bustype = BUS_HOST; | ||
137 | input->id.vendor = 0x0001; | ||
138 | input->id.product = 0x0001; | ||
139 | input->id.version = 0x0100; | ||
140 | |||
141 | if (rotary->up_key) { | ||
142 | __set_bit(EV_KEY, input->evbit); | ||
143 | __set_bit(rotary->up_key, input->keybit); | ||
144 | __set_bit(rotary->down_key, input->keybit); | ||
145 | } else { | ||
146 | __set_bit(EV_REL, input->evbit); | ||
147 | __set_bit(rotary->rel_code, input->relbit); | ||
148 | } | ||
149 | |||
150 | if (rotary->button_key) { | ||
151 | __set_bit(EV_KEY, input->evbit); | ||
152 | __set_bit(rotary->button_key, input->keybit); | ||
153 | } | ||
154 | |||
155 | error = request_irq(rotary->irq, bfin_rotary_isr, | ||
156 | 0, dev_name(&pdev->dev), pdev); | ||
157 | if (error) { | ||
158 | dev_err(&pdev->dev, | ||
159 | "unable to claim irq %d; error %d\n", | ||
160 | rotary->irq, error); | ||
161 | goto out1; | ||
162 | } | ||
163 | |||
164 | error = input_register_device(input); | ||
165 | if (error) { | ||
166 | dev_err(&pdev->dev, | ||
167 | "unable to register input device (%d)\n", error); | ||
168 | goto out2; | ||
169 | } | ||
170 | |||
171 | if (pdata->rotary_button_key) | ||
172 | bfin_write_CNT_IMASK(CZMIE); | ||
173 | |||
174 | if (pdata->mode & ROT_DEBE) | ||
175 | bfin_write_CNT_DEBOUNCE(pdata->debounce & DPRESCALE); | ||
176 | |||
177 | if (pdata->mode) | ||
178 | bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | | ||
179 | (pdata->mode & ~CNTE)); | ||
180 | |||
181 | bfin_write_CNT_IMASK(bfin_read_CNT_IMASK() | UCIE | DCIE); | ||
182 | bfin_write_CNT_CONFIG(bfin_read_CNT_CONFIG() | CNTE); | ||
183 | |||
184 | platform_set_drvdata(pdev, rotary); | ||
185 | device_init_wakeup(&pdev->dev, 1); | ||
186 | |||
187 | return 0; | ||
188 | |||
189 | out2: | ||
190 | free_irq(rotary->irq, pdev); | ||
191 | out1: | ||
192 | input_free_device(input); | ||
193 | kfree(rotary); | ||
194 | peripheral_free_list(per_cnt); | ||
195 | |||
196 | return error; | ||
197 | } | ||
198 | |||
199 | static int __devexit bfin_rotary_remove(struct platform_device *pdev) | ||
200 | { | ||
201 | struct bfin_rot *rotary = platform_get_drvdata(pdev); | ||
202 | |||
203 | bfin_write_CNT_CONFIG(0); | ||
204 | bfin_write_CNT_IMASK(0); | ||
205 | |||
206 | free_irq(rotary->irq, pdev); | ||
207 | input_unregister_device(rotary->input); | ||
208 | peripheral_free_list(per_cnt); | ||
209 | |||
210 | kfree(rotary); | ||
211 | platform_set_drvdata(pdev, NULL); | ||
212 | |||
213 | return 0; | ||
214 | } | ||
215 | |||
216 | #ifdef CONFIG_PM | ||
217 | static int bfin_rotary_suspend(struct device *dev) | ||
218 | { | ||
219 | struct platform_device *pdev = to_platform_device(dev); | ||
220 | struct bfin_rot *rotary = platform_get_drvdata(pdev); | ||
221 | |||
222 | rotary->cnt_config = bfin_read_CNT_CONFIG(); | ||
223 | rotary->cnt_imask = bfin_read_CNT_IMASK(); | ||
224 | rotary->cnt_debounce = bfin_read_CNT_DEBOUNCE(); | ||
225 | |||
226 | if (device_may_wakeup(&pdev->dev)) | ||
227 | enable_irq_wake(rotary->irq); | ||
228 | |||
229 | return 0; | ||
230 | } | ||
231 | |||
232 | static int bfin_rotary_resume(struct device *dev) | ||
233 | { | ||
234 | struct platform_device *pdev = to_platform_device(dev); | ||
235 | struct bfin_rot *rotary = platform_get_drvdata(pdev); | ||
236 | |||
237 | bfin_write_CNT_DEBOUNCE(rotary->cnt_debounce); | ||
238 | bfin_write_CNT_IMASK(rotary->cnt_imask); | ||
239 | bfin_write_CNT_CONFIG(rotary->cnt_config & ~CNTE); | ||
240 | |||
241 | if (device_may_wakeup(&pdev->dev)) | ||
242 | disable_irq_wake(rotary->irq); | ||
243 | |||
244 | if (rotary->cnt_config & CNTE) | ||
245 | bfin_write_CNT_CONFIG(rotary->cnt_config); | ||
246 | |||
247 | return 0; | ||
248 | } | ||
249 | |||
250 | static struct dev_pm_ops bfin_rotary_pm_ops = { | ||
251 | .suspend = bfin_rotary_suspend, | ||
252 | .resume = bfin_rotary_resume, | ||
253 | }; | ||
254 | #endif | ||
255 | |||
256 | static struct platform_driver bfin_rotary_device_driver = { | ||
257 | .probe = bfin_rotary_probe, | ||
258 | .remove = __devexit_p(bfin_rotary_remove), | ||
259 | .driver = { | ||
260 | .name = "bfin-rotary", | ||
261 | .owner = THIS_MODULE, | ||
262 | #ifdef CONFIG_PM | ||
263 | .pm = &bfin_rotary_pm_ops, | ||
264 | #endif | ||
265 | }, | ||
266 | }; | ||
267 | |||
268 | static int __init bfin_rotary_init(void) | ||
269 | { | ||
270 | return platform_driver_register(&bfin_rotary_device_driver); | ||
271 | } | ||
272 | module_init(bfin_rotary_init); | ||
273 | |||
274 | static void __exit bfin_rotary_exit(void) | ||
275 | { | ||
276 | platform_driver_unregister(&bfin_rotary_device_driver); | ||
277 | } | ||
278 | module_exit(bfin_rotary_exit); | ||
279 | |||
280 | MODULE_LICENSE("GPL"); | ||
281 | MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>"); | ||
282 | MODULE_DESCRIPTION("Rotary Counter driver for Blackfin Processors"); | ||
283 | MODULE_ALIAS("platform:bfin-rotary"); | ||