Commit 5a90e5bca96696f1daa0bb0a9db299eb40241ada
Committed by
Dmitry Torokhov
1 parent
867d2682e9
Exists in
master
and in
7 other branches
Input: add support for PXA27x keyboard controller
Signed-off-by: Rodolfo Giometti <giometti@enneenne.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Showing 4 changed files with 281 additions and 0 deletions Side-by-side Diff
drivers/input/keyboard/Kconfig
... | ... | @@ -203,6 +203,15 @@ |
203 | 203 | To compile this driver as a module, choose M here: the |
204 | 204 | module will be called omap-keypad. |
205 | 205 | |
206 | +config KEYBOARD_PXA27x | |
207 | + tristate "PXA27x keyboard support" | |
208 | + depends on PXA27x | |
209 | + help | |
210 | + Enable support for PXA27x matrix keyboard controller | |
211 | + | |
212 | + To compile this driver as a module, choose M here: the | |
213 | + module will be called pxa27x_keyboard. | |
214 | + | |
206 | 215 | config KEYBOARD_AAED2000 |
207 | 216 | tristate "AAED-2000 keyboard" |
208 | 217 | depends on MACH_AAED2000 |
drivers/input/keyboard/Makefile
... | ... | @@ -17,6 +17,7 @@ |
17 | 17 | obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o |
18 | 18 | obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o |
19 | 19 | obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o |
20 | +obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keyboard.o | |
20 | 21 | obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o |
21 | 22 | obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o |
drivers/input/keyboard/pxa27x_keyboard.c
1 | +/* | |
2 | + * linux/drivers/input/keyboard/pxa27x_keyboard.c | |
3 | + * | |
4 | + * Driver for the pxa27x matrix keyboard controller. | |
5 | + * | |
6 | + * Created: Feb 22, 2007 | |
7 | + * Author: Rodolfo Giometti <giometti@linux.it> | |
8 | + * | |
9 | + * Based on a previous implementations by Kevin O'Connor | |
10 | + * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and | |
11 | + * on some suggestions by Nicolas Pitre <nico@cam.org>. | |
12 | + * | |
13 | + * This program is free software; you can redistribute it and/or modify | |
14 | + * it under the terms of the GNU General Public License version 2 as | |
15 | + * published by the Free Software Foundation. | |
16 | + */ | |
17 | + | |
18 | + | |
19 | +#include <linux/kernel.h> | |
20 | +#include <linux/module.h> | |
21 | +#include <linux/init.h> | |
22 | +#include <linux/interrupt.h> | |
23 | +#include <linux/input.h> | |
24 | +#include <linux/device.h> | |
25 | +#include <linux/platform_device.h> | |
26 | + | |
27 | +#include <asm/mach-types.h> | |
28 | +#include <asm/mach/arch.h> | |
29 | +#include <asm/mach/map.h> | |
30 | + | |
31 | +#include <asm/arch/hardware.h> | |
32 | +#include <asm/arch/pxa-regs.h> | |
33 | +#include <asm/arch/irqs.h> | |
34 | +#include <asm/arch/pxa27x_keyboard.h> | |
35 | + | |
36 | +#define DRIVER_NAME "pxa27x-keyboard" | |
37 | + | |
38 | +#define KPASMKP(col) (col/2 == 0 ? KPASMKP0 : \ | |
39 | + col/2 == 1 ? KPASMKP1 : \ | |
40 | + col/2 == 2 ? KPASMKP2 : KPASMKP3) | |
41 | +#define KPASMKPx_MKC(row, col) (1 << (row + 16 * (col % 2))) | |
42 | + | |
43 | +static irqreturn_t pxakbd_irq_handler(int irq, void *dev_id) | |
44 | +{ | |
45 | + struct platform_device *pdev = dev_id; | |
46 | + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; | |
47 | + struct input_dev *input_dev = platform_get_drvdata(pdev); | |
48 | + unsigned long kpc = KPC; | |
49 | + int p, row, col, rel; | |
50 | + | |
51 | + if (kpc & KPC_DI) { | |
52 | + unsigned long kpdk = KPDK; | |
53 | + | |
54 | + if (!(kpdk & KPDK_DKP)) { | |
55 | + /* better luck next time */ | |
56 | + } else if (kpc & KPC_REE0) { | |
57 | + unsigned long kprec = KPREC; | |
58 | + KPREC = 0x7f; | |
59 | + | |
60 | + if (kprec & KPREC_OF0) | |
61 | + rel = (kprec & 0xff) + 0x7f; | |
62 | + else if (kprec & KPREC_UF0) | |
63 | + rel = (kprec & 0xff) - 0x7f - 0xff; | |
64 | + else | |
65 | + rel = (kprec & 0xff) - 0x7f; | |
66 | + | |
67 | + if (rel) { | |
68 | + input_report_rel(input_dev, REL_WHEEL, rel); | |
69 | + input_sync(input_dev); | |
70 | + } | |
71 | + } | |
72 | + } | |
73 | + | |
74 | + if (kpc & KPC_MI) { | |
75 | + /* report the status of every button */ | |
76 | + for (row = 0; row < pdata->nr_rows; row++) { | |
77 | + for (col = 0; col < pdata->nr_cols; col++) { | |
78 | + p = KPASMKP(col) & KPASMKPx_MKC(row, col) ? | |
79 | + 1 : 0; | |
80 | + pr_debug("keycode %x - pressed %x\n", | |
81 | + pdata->keycodes[row][col], p); | |
82 | + input_report_key(input_dev, | |
83 | + pdata->keycodes[row][col], p); | |
84 | + } | |
85 | + } | |
86 | + input_sync(input_dev); | |
87 | + } | |
88 | + | |
89 | + return IRQ_HANDLED; | |
90 | +} | |
91 | + | |
92 | +static int pxakbd_open(struct input_dev *dev) | |
93 | +{ | |
94 | + /* Set keypad control register */ | |
95 | + KPC |= (KPC_ASACT | | |
96 | + KPC_MS_ALL | | |
97 | + (2 << 6) | KPC_REE0 | KPC_DK_DEB_SEL | | |
98 | + KPC_ME | KPC_MIE | KPC_DE | KPC_DIE); | |
99 | + | |
100 | + KPC &= ~KPC_AS; /* disable automatic scan */ | |
101 | + KPC &= ~KPC_IMKP; /* do not ignore multiple keypresses */ | |
102 | + | |
103 | + /* Set rotary count to mid-point value */ | |
104 | + KPREC = 0x7F; | |
105 | + | |
106 | + /* Enable unit clock */ | |
107 | + pxa_set_cken(CKEN19_KEYPAD, 1); | |
108 | + | |
109 | + return 0; | |
110 | +} | |
111 | + | |
112 | +static void pxakbd_close(struct input_dev *dev) | |
113 | +{ | |
114 | + /* Disable clock unit */ | |
115 | + pxa_set_cken(CKEN19_KEYPAD, 0); | |
116 | +} | |
117 | + | |
118 | +#ifdef CONFIG_PM | |
119 | +static int pxakbd_suspend(struct platform_device *pdev, pm_message_t state) | |
120 | +{ | |
121 | + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; | |
122 | + | |
123 | + /* Save controller status */ | |
124 | + pdata->reg_kpc = KPC; | |
125 | + pdata->reg_kprec = KPREC; | |
126 | + | |
127 | + return 0; | |
128 | +} | |
129 | + | |
130 | +static int pxakbd_resume(struct platform_device *pdev) | |
131 | +{ | |
132 | + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; | |
133 | + struct input_dev *input_dev = platform_get_drvdata(pdev); | |
134 | + | |
135 | + mutex_lock(&input_dev->mutex); | |
136 | + | |
137 | + if (input_dev->users) { | |
138 | + /* Restore controller status */ | |
139 | + KPC = pdata->reg_kpc; | |
140 | + KPREC = pdata->reg_kprec; | |
141 | + | |
142 | + /* Enable unit clock */ | |
143 | + pxa_set_cken(CKEN19_KEYPAD, 1); | |
144 | + } | |
145 | + | |
146 | + mutex_unlock(&input_dev->mutex); | |
147 | + | |
148 | + return 0; | |
149 | +} | |
150 | +#else | |
151 | +#define pxakbd_suspend NULL | |
152 | +#define pxakbd_resume NULL | |
153 | +#endif | |
154 | + | |
155 | +static int __devinit pxakbd_probe(struct platform_device *pdev) | |
156 | +{ | |
157 | + struct pxa27x_keyboard_platform_data *pdata = pdev->dev.platform_data; | |
158 | + struct input_dev *input_dev; | |
159 | + int i, row, col, error; | |
160 | + | |
161 | + /* Create and register the input driver. */ | |
162 | + input_dev = input_allocate_device(); | |
163 | + if (!input_dev) { | |
164 | + printk(KERN_ERR "Cannot request keypad device\n"); | |
165 | + return -ENOMEM; | |
166 | + } | |
167 | + | |
168 | + input_dev->name = DRIVER_NAME; | |
169 | + input_dev->id.bustype = BUS_HOST; | |
170 | + input_dev->open = pxakbd_open; | |
171 | + input_dev->close = pxakbd_close; | |
172 | + input_dev->cdev.dev = &pdev->dev; | |
173 | + | |
174 | + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_REL); | |
175 | + input_dev->relbit[LONG(REL_WHEEL)] = BIT(REL_WHEEL); | |
176 | + for (row = 0; row < pdata->nr_rows; row++) { | |
177 | + for (col = 0; col < pdata->nr_cols; col++) { | |
178 | + int code = pdata->keycodes[row][col]; | |
179 | + if (code > 0) | |
180 | + set_bit(code, input_dev->keybit); | |
181 | + } | |
182 | + } | |
183 | + | |
184 | + error = request_irq(IRQ_KEYPAD, pxakbd_irq_handler, SA_INTERRUPT, | |
185 | + DRIVER_NAME, pdev); | |
186 | + if (error) { | |
187 | + printk(KERN_ERR "Cannot request keypad IRQ\n"); | |
188 | + pxa_set_cken(CKEN19_KEYPAD, 0); | |
189 | + goto err_free_dev; | |
190 | + } | |
191 | + | |
192 | + platform_set_drvdata(pdev, input_dev); | |
193 | + | |
194 | + /* Register the input device */ | |
195 | + error = input_register_device(input_dev); | |
196 | + if (error) | |
197 | + goto err_free_irq; | |
198 | + | |
199 | + /* Setup GPIOs. */ | |
200 | + for (i = 0; i < pdata->nr_rows + pdata->nr_cols; i++) | |
201 | + pxa_gpio_mode(pdata->gpio_modes[i]); | |
202 | + | |
203 | + /* | |
204 | + * Store rows/cols info into keyboard registers. | |
205 | + */ | |
206 | + | |
207 | + KPC |= (pdata->nr_rows - 1) << 26; | |
208 | + KPC |= (pdata->nr_cols - 1) << 23; | |
209 | + | |
210 | + for (col = 0; col < pdata->nr_cols; col++) | |
211 | + KPC |= KPC_MS0 << col; | |
212 | + | |
213 | + return 0; | |
214 | + | |
215 | + err_free_irq: | |
216 | + platform_set_drvdata(pdev, NULL); | |
217 | + free_irq(IRQ_KEYPAD, pdev); | |
218 | + err_free_dev: | |
219 | + input_free_device(input_dev); | |
220 | + return error; | |
221 | +} | |
222 | + | |
223 | +static int __devexit pxakbd_remove(struct platform_device *pdev) | |
224 | +{ | |
225 | + struct input_dev *input_dev = platform_get_drvdata(pdev); | |
226 | + | |
227 | + input_unregister_device(input_dev); | |
228 | + free_irq(IRQ_KEYPAD, pdev); | |
229 | + platform_set_drvdata(pdev, NULL); | |
230 | + | |
231 | + return 0; | |
232 | +} | |
233 | + | |
234 | +static struct platform_driver pxakbd_driver = { | |
235 | + .probe = pxakbd_probe, | |
236 | + .remove = __devexit_p(pxakbd_remove), | |
237 | + .suspend = pxakbd_suspend, | |
238 | + .resume = pxakbd_resume, | |
239 | + .driver = { | |
240 | + .name = DRIVER_NAME, | |
241 | + }, | |
242 | +}; | |
243 | + | |
244 | +static int __init pxakbd_init(void) | |
245 | +{ | |
246 | + return platform_driver_register(&pxakbd_driver); | |
247 | +} | |
248 | + | |
249 | +static void __exit pxakbd_exit(void) | |
250 | +{ | |
251 | + platform_driver_unregister(&pxakbd_driver); | |
252 | +} | |
253 | + | |
254 | +module_init(pxakbd_init); | |
255 | +module_exit(pxakbd_exit); | |
256 | + | |
257 | +MODULE_DESCRIPTION("PXA27x Matrix Keyboard Driver"); | |
258 | +MODULE_LICENSE("GPL"); |
include/asm-arm/arch-pxa/pxa27x_keyboard.h
1 | +#define PXAKBD_MAXROW 8 | |
2 | +#define PXAKBD_MAXCOL 8 | |
3 | + | |
4 | +struct pxa27x_keyboard_platform_data { | |
5 | + int nr_rows, nr_cols; | |
6 | + int keycodes[PXAKBD_MAXROW][PXAKBD_MAXCOL]; | |
7 | + int gpio_modes[PXAKBD_MAXROW + PXAKBD_MAXCOL]; | |
8 | + | |
9 | +#ifdef CONFIG_PM | |
10 | + u32 reg_kpc; | |
11 | + u32 reg_kprec; | |
12 | +#endif | |
13 | +}; |