aboutsummaryrefslogtreecommitdiffstats
path: root/drivers
diff options
context:
space:
mode:
authorSundar Iyer <sundar.iyer@stericsson.com>2010-09-29 22:42:14 -0400
committerDmitry Torokhov <dmitry.torokhov@gmail.com>2010-10-13 10:49:24 -0400
commit1158f0f16224068e27e384c1a27218aa00243967 (patch)
treea5b5c255d8fe21cdd9948f64ce22acae8af0f6c3 /drivers
parenta9cc0202227caa4adb5e823d5fbce7790364941f (diff)
Input: add support for Nomadik SKE keypad controller
Add support for the keypad controller in the Scroll Key Encoder (SKE) module on the Nomadik family and the DB8500 SoC. Acked-by: Linus Walleij <linus.walleij@stericsson.com> Signed-off-by: Sundar Iyer <sundar.iyer@stericsson.com> Signed-off-by: Dmitry Torokhov <dtor@mail.ru>
Diffstat (limited to 'drivers')
-rw-r--r--drivers/input/keyboard/Kconfig10
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/nomadik-ske-keypad.c408
3 files changed, 419 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 4f300488e1f3..d63f5664e3fb 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -327,6 +327,16 @@ config KEYBOARD_NEWTON
327 To compile this driver as a module, choose M here: the 327 To compile this driver as a module, choose M here: the
328 module will be called newtonkbd. 328 module will be called newtonkbd.
329 329
330config KEYBOARD_NOMADIK
331 tristate "ST-Ericsson Nomadik SKE keyboard"
332 depends on PLAT_NOMADIK
333 help
334 Say Y here if you want to use a keypad provided on the SKE controller
335 used on the Ux500 and Nomadik platforms
336
337 To compile this driver as a module, choose M here: the
338 module will be called nmk-ske-keypad.
339
330config KEYBOARD_OPENCORES 340config KEYBOARD_OPENCORES
331 tristate "OpenCores Keyboard Controller" 341 tristate "OpenCores Keyboard Controller"
332 help 342 help
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 8ac01fb642c1..c13809c5c3a7 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -28,6 +28,7 @@ obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
28obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o 28obj-$(CONFIG_KEYBOARD_MAX7359) += max7359_keypad.o
29obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o 29obj-$(CONFIG_KEYBOARD_MCS) += mcs_touchkey.o
30obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o 30obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
31obj-$(CONFIG_KEYBOARD_NOMADIK) += nomadik-ske-keypad.o
31obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o 32obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
32obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o 33obj-$(CONFIG_KEYBOARD_OMAP4) += omap4-keypad.o
33obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o 34obj-$(CONFIG_KEYBOARD_OPENCORES) += opencores-kbd.o
diff --git a/drivers/input/keyboard/nomadik-ske-keypad.c b/drivers/input/keyboard/nomadik-ske-keypad.c
new file mode 100644
index 000000000000..6e0f23091360
--- /dev/null
+++ b/drivers/input/keyboard/nomadik-ske-keypad.c
@@ -0,0 +1,408 @@
1/*
2 * Copyright (C) ST-Ericsson SA 2010
3 *
4 * Author: Naveen Kumar G <naveen.gaddipati@stericsson.com> for ST-Ericsson
5 * Author: Sundar Iyer <sundar.iyer@stericsson.com> for ST-Ericsson
6 *
7 * License terms:GNU General Public License (GPL) version 2
8 *
9 * Keypad controller driver for the SKE (Scroll Key Encoder) module used in
10 * the Nomadik 8815 and Ux500 platforms.
11 */
12
13#include <linux/platform_device.h>
14#include <linux/interrupt.h>
15#include <linux/spinlock.h>
16#include <linux/io.h>
17#include <linux/delay.h>
18#include <linux/input.h>
19#include <linux/slab.h>
20#include <linux/clk.h>
21
22#include <plat/ske.h>
23
24/* SKE_CR bits */
25#define SKE_KPMLT (0x1 << 6)
26#define SKE_KPCN (0x7 << 3)
27#define SKE_KPASEN (0x1 << 2)
28#define SKE_KPASON (0x1 << 7)
29
30/* SKE_IMSC bits */
31#define SKE_KPIMA (0x1 << 2)
32
33/* SKE_ICR bits */
34#define SKE_KPICS (0x1 << 3)
35#define SKE_KPICA (0x1 << 2)
36
37/* SKE_RIS bits */
38#define SKE_KPRISA (0x1 << 2)
39
40#define SKE_KEYPAD_ROW_SHIFT 3
41#define SKE_KPD_KEYMAP_SIZE (8 * 8)
42
43/* keypad auto scan registers */
44#define SKE_ASR0 0x20
45#define SKE_ASR1 0x24
46#define SKE_ASR2 0x28
47#define SKE_ASR3 0x2C
48
49#define SKE_NUM_ASRX_REGISTERS (4)
50
51/**
52 * struct ske_keypad - data structure used by keypad driver
53 * @irq: irq no
54 * @reg_base: ske regsiters base address
55 * @input: pointer to input device object
56 * @board: keypad platform device
57 * @keymap: matrix scan code table for keycodes
58 * @clk: clock structure pointer
59 */
60struct ske_keypad {
61 int irq;
62 void __iomem *reg_base;
63 struct input_dev *input;
64 const struct ske_keypad_platform_data *board;
65 unsigned short keymap[SKE_KPD_KEYMAP_SIZE];
66 struct clk *clk;
67 spinlock_t ske_keypad_lock;
68};
69
70static void ske_keypad_set_bits(struct ske_keypad *keypad, u16 addr,
71 u8 mask, u8 data)
72{
73 u32 ret;
74
75 spin_lock(&keypad->ske_keypad_lock);
76
77 ret = readl(keypad->reg_base + addr);
78 ret &= ~mask;
79 ret |= data;
80 writel(ret, keypad->reg_base + addr);
81
82 spin_unlock(&keypad->ske_keypad_lock);
83}
84
85/*
86 * ske_keypad_chip_init: init keypad controller configuration
87 *
88 * Enable Multi key press detection, auto scan mode
89 */
90static int __devinit ske_keypad_chip_init(struct ske_keypad *keypad)
91{
92 u32 value;
93 int timeout = 50;
94
95 /* check SKE_RIS to be 0 */
96 while ((readl(keypad->reg_base + SKE_RIS) != 0x00000000) && timeout--)
97 cpu_relax();
98
99 if (!timeout)
100 return -EINVAL;
101
102 /*
103 * set debounce value
104 * keypad dbounce is configured in DBCR[15:8]
105 * dbounce value in steps of 32/32.768 ms
106 */
107 spin_lock(&keypad->ske_keypad_lock);
108 value = readl(keypad->reg_base + SKE_DBCR);
109 value = value & 0xff;
110 value |= ((keypad->board->debounce_ms * 32000)/32768) << 8;
111 writel(value, keypad->reg_base + SKE_DBCR);
112 spin_unlock(&keypad->ske_keypad_lock);
113
114 /* enable multi key detection */
115 ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPMLT);
116
117 /*
118 * set up the number of columns
119 * KPCN[5:3] defines no. of keypad columns to be auto scanned
120 */
121 value = (keypad->board->kcol - 1) << 3;
122 ske_keypad_set_bits(keypad, SKE_CR, SKE_KPCN, value);
123
124 /* clear keypad interrupt for auto(and pending SW) scans */
125 ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA | SKE_KPICS);
126
127 /* un-mask keypad interrupts */
128 ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
129
130 /* enable automatic scan */
131 ske_keypad_set_bits(keypad, SKE_CR, 0x0, SKE_KPASEN);
132
133 return 0;
134}
135
136static void ske_keypad_read_data(struct ske_keypad *keypad)
137{
138 struct input_dev *input = keypad->input;
139 u16 status;
140 int col = 0, row = 0, code;
141 int ske_asr, ske_ris, key_pressed, i;
142
143 /*
144 * Read the auto scan registers
145 *
146 * Each SKE_ASRx (x=0 to x=3) contains two row values.
147 * lower byte contains row value for column 2*x,
148 * upper byte contains row value for column 2*x + 1
149 */
150 for (i = 0; i < SKE_NUM_ASRX_REGISTERS; i++) {
151 ske_asr = readl(keypad->reg_base + SKE_ASR0 + (4 * i));
152 if (!ske_asr)
153 continue;
154
155 /* now that ASRx is zero, find out the column x and row y*/
156 if (ske_asr & 0xff) {
157 col = i * 2;
158 status = ske_asr & 0xff;
159 } else {
160 col = (i * 2) + 1;
161 status = (ske_asr & 0xff00) >> 8;
162 }
163
164 /* find out the row */
165 row = __ffs(status);
166
167 code = MATRIX_SCAN_CODE(row, col, SKE_KEYPAD_ROW_SHIFT);
168 ske_ris = readl(keypad->reg_base + SKE_RIS);
169 key_pressed = ske_ris & SKE_KPRISA;
170
171 input_event(input, EV_MSC, MSC_SCAN, code);
172 input_report_key(input, keypad->keymap[code], key_pressed);
173 input_sync(input);
174 }
175}
176
177static irqreturn_t ske_keypad_irq(int irq, void *dev_id)
178{
179 struct ske_keypad *keypad = dev_id;
180 int retries = 20;
181
182 /* disable auto scan interrupt; mask the interrupt generated */
183 ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
184 ske_keypad_set_bits(keypad, SKE_ICR, 0x0, SKE_KPICA);
185
186 while ((readl(keypad->reg_base + SKE_CR) & SKE_KPASON) && --retries)
187 msleep(5);
188
189 if (retries) {
190 /* SKEx registers are stable and can be read */
191 ske_keypad_read_data(keypad);
192 }
193
194 /* enable auto scan interrupts */
195 ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
196
197 return IRQ_HANDLED;
198}
199
200static int __devinit ske_keypad_probe(struct platform_device *pdev)
201{
202 const struct ske_keypad_platform_data *plat = pdev->dev.platform_data;
203 struct ske_keypad *keypad;
204 struct input_dev *input;
205 struct resource *res;
206 int irq;
207 int error;
208
209 if (!plat) {
210 dev_err(&pdev->dev, "invalid keypad platform data\n");
211 return -EINVAL;
212 }
213
214 irq = platform_get_irq(pdev, 0);
215 if (irq < 0) {
216 dev_err(&pdev->dev, "failed to get keypad irq\n");
217 return -EINVAL;
218 }
219
220 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
221 if (!res) {
222 dev_err(&pdev->dev, "missing platform resources\n");
223 return -EINVAL;
224 }
225
226 keypad = kzalloc(sizeof(struct ske_keypad), GFP_KERNEL);
227 input = input_allocate_device();
228 if (!keypad || !input) {
229 dev_err(&pdev->dev, "failed to allocate keypad memory\n");
230 error = -ENOMEM;
231 goto err_free_mem;
232 }
233
234 keypad->irq = irq;
235 keypad->board = plat;
236 keypad->input = input;
237 spin_lock_init(&keypad->ske_keypad_lock);
238
239 if (!request_mem_region(res->start, resource_size(res), pdev->name)) {
240 dev_err(&pdev->dev, "failed to request I/O memory\n");
241 error = -EBUSY;
242 goto err_free_mem;
243 }
244
245 keypad->reg_base = ioremap(res->start, resource_size(res));
246 if (!keypad->reg_base) {
247 dev_err(&pdev->dev, "failed to remap I/O memory\n");
248 error = -ENXIO;
249 goto err_free_mem_region;
250 }
251
252 keypad->clk = clk_get(&pdev->dev, NULL);
253 if (IS_ERR(keypad->clk)) {
254 dev_err(&pdev->dev, "failed to get clk\n");
255 error = PTR_ERR(keypad->clk);
256 goto err_iounmap;
257 }
258
259 input->id.bustype = BUS_HOST;
260 input->name = "ux500-ske-keypad";
261 input->dev.parent = &pdev->dev;
262
263 input->keycode = keypad->keymap;
264 input->keycodesize = sizeof(keypad->keymap[0]);
265 input->keycodemax = ARRAY_SIZE(keypad->keymap);
266
267 input_set_capability(input, EV_MSC, MSC_SCAN);
268
269 __set_bit(EV_KEY, input->evbit);
270 if (!plat->no_autorepeat)
271 __set_bit(EV_REP, input->evbit);
272
273 matrix_keypad_build_keymap(plat->keymap_data, SKE_KEYPAD_ROW_SHIFT,
274 input->keycode, input->keybit);
275
276 clk_enable(keypad->clk);
277
278 /* go through board initialization helpers */
279 if (keypad->board->init)
280 keypad->board->init();
281
282 error = ske_keypad_chip_init(keypad);
283 if (error) {
284 dev_err(&pdev->dev, "unable to init keypad hardware\n");
285 goto err_clk_disable;
286 }
287
288 error = request_threaded_irq(keypad->irq, NULL, ske_keypad_irq,
289 IRQF_ONESHOT, "ske-keypad", keypad);
290 if (error) {
291 dev_err(&pdev->dev, "allocate irq %d failed\n", keypad->irq);
292 goto err_clk_disable;
293 }
294
295 error = input_register_device(input);
296 if (error) {
297 dev_err(&pdev->dev,
298 "unable to register input device: %d\n", error);
299 goto err_free_irq;
300 }
301
302 if (plat->wakeup_enable)
303 device_init_wakeup(&pdev->dev, true);
304
305 platform_set_drvdata(pdev, keypad);
306
307 return 0;
308
309err_free_irq:
310 free_irq(keypad->irq, keypad);
311err_clk_disable:
312 clk_disable(keypad->clk);
313 clk_put(keypad->clk);
314err_iounmap:
315 iounmap(keypad->reg_base);
316err_free_mem_region:
317 release_mem_region(res->start, resource_size(res));
318err_free_mem:
319 input_free_device(input);
320 kfree(keypad);
321 return error;
322}
323
324static int __devexit ske_keypad_remove(struct platform_device *pdev)
325{
326 struct ske_keypad *keypad = platform_get_drvdata(pdev);
327 struct resource *res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
328
329 free_irq(keypad->irq, keypad);
330
331 input_unregister_device(keypad->input);
332
333 clk_disable(keypad->clk);
334 clk_put(keypad->clk);
335
336 if (keypad->board->exit)
337 keypad->board->exit();
338
339 iounmap(keypad->reg_base);
340 release_mem_region(res->start, resource_size(res));
341 kfree(keypad);
342
343 return 0;
344}
345
346#ifdef CONFIG_PM
347static int ske_keypad_suspend(struct device *dev)
348{
349 struct platform_device *pdev = to_platform_device(dev);
350 struct ske_keypad *keypad = platform_get_drvdata(pdev);
351 int irq = platform_get_irq(pdev, 0);
352
353 if (device_may_wakeup(dev))
354 enable_irq_wake(irq);
355 else
356 ske_keypad_set_bits(keypad, SKE_IMSC, ~SKE_KPIMA, 0x0);
357
358 return 0;
359}
360
361static int ske_keypad_resume(struct device *dev)
362{
363 struct platform_device *pdev = to_platform_device(dev);
364 struct ske_keypad *keypad = platform_get_drvdata(pdev);
365 int irq = platform_get_irq(pdev, 0);
366
367 if (device_may_wakeup(dev))
368 disable_irq_wake(irq);
369 else
370 ske_keypad_set_bits(keypad, SKE_IMSC, 0x0, SKE_KPIMA);
371
372 return 0;
373}
374
375static const struct dev_pm_ops ske_keypad_dev_pm_ops = {
376 .suspend = ske_keypad_suspend,
377 .resume = ske_keypad_resume,
378};
379#endif
380
381struct platform_driver ske_keypad_driver = {
382 .driver = {
383 .name = "nmk-ske-keypad",
384 .owner = THIS_MODULE,
385#ifdef CONFIG_PM
386 .pm = &ske_keypad_dev_pm_ops,
387#endif
388 },
389 .probe = ske_keypad_probe,
390 .remove = __devexit_p(ske_keypad_remove),
391};
392
393static int __init ske_keypad_init(void)
394{
395 return platform_driver_probe(&ske_keypad_driver, ske_keypad_probe);
396}
397module_init(ske_keypad_init);
398
399static void __exit ske_keypad_exit(void)
400{
401 platform_driver_unregister(&ske_keypad_driver);
402}
403module_exit(ske_keypad_exit);
404
405MODULE_LICENSE("GPL v2");
406MODULE_AUTHOR("Naveen Kumar <naveen.gaddipati@stericsson.com> / Sundar Iyer <sundar.iyer@stericsson.com>");
407MODULE_DESCRIPTION("Nomadik Scroll-Key-Encoder Keypad Driver");
408MODULE_ALIAS("platform:nomadik-ske-keypad");