1 drivers/input/keyboard/Kconfig | 12 -
2 drivers/input/keyboard/Makefile | 1
3 drivers/input/keyboard/tosakbd.c | 467 +++++++++++++++++++++++++++++++++++++++
4 3 files changed, 479 insertions(+), 1 deletion(-)
6 Index: git/drivers/input/keyboard/Kconfig
7 ===================================================================
8 --- git.orig/drivers/input/keyboard/Kconfig 2006-10-31 16:08:57.000000000 +0000
9 +++ git/drivers/input/keyboard/Kconfig 2006-11-07 22:13:10.000000000 +0000
10 @@ -148,12 +148,22 @@ config KEYBOARD_SPITZ
11 depends on PXA_SHARPSL
14 - Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
15 + Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
16 SL-C3000 and Sl-C3100 series of PDAs.
18 To compile this driver as a module, choose M here: the
19 module will be called spitzkbd.
22 + tristate "Tosa keyboard"
23 + depends on PXA_SHARPSL
26 + Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
28 + To compile this driver as a module, choose M here: the
29 + module will be called tosakbd.
32 tristate "Amiga keyboard"
34 Index: git/drivers/input/keyboard/Makefile
35 ===================================================================
36 --- git.orig/drivers/input/keyboard/Makefile 2006-10-31 16:08:57.000000000 +0000
37 +++ git/drivers/input/keyboard/Makefile 2006-11-07 22:13:10.000000000 +0000
38 @@ -17,4 +17,5 @@ obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkb
39 obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
40 obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
41 obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
42 +obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
44 Index: git/drivers/input/keyboard/tosakbd.c
45 ===================================================================
46 --- /dev/null 1970-01-01 00:00:00.000000000 +0000
47 +++ git/drivers/input/keyboard/tosakbd.c 2006-11-07 23:27:19.000000000 +0000
50 + * Keyboard driver for Sharp Tosa models (SL-6000x)
52 + * Copyright (c) 2005 Dirk Opfer
54 + * Based on xtkbd.c/locomkbd.c/corgikbd.c
56 + * This program is free software; you can redistribute it and/or modify
57 + * it under the terms of the GNU General Public License version 2 as
58 + * published by the Free Software Foundation.
62 +#include <linux/delay.h>
63 +#include <linux/platform_device.h>
64 +#include <linux/init.h>
65 +#include <linux/input.h>
66 +#include <linux/interrupt.h>
67 +#include <linux/jiffies.h>
68 +#include <linux/module.h>
69 +#include <linux/slab.h>
71 +#include <asm/arch/tosa.h>
72 +#include <asm/arch/hardware.h>
73 +#include <asm/arch/pxa-regs.h>
76 +#define TOSA_KEY_STROBE_NUM (11)
77 +#define TOSA_KEY_SENSE_NUM (7)
79 +#define KEYMASK_ON (0x1<<0)
80 +#define KEYMASK_REC (0x1<<1)
81 +#define KEYMASK_SYNC (0x1<<2)
85 +#define KB_ROWMASK(r) (1 << (r))
86 +#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
87 +#define NR_SCANCODES (SCANCODE(KB_ROWS-1,KB_COLS)+1+1)
89 +#define SCAN_INTERVAL (HZ/10)
90 +#define HP_SCAN_INTERVAL (150) /* ms */
91 +#define HP_STABLE_COUNT 2
93 +#define TOSA_KEY_CALENDER KEY_F1
94 +#define TOSA_KEY_ADDRESS KEY_F2
95 +#define TOSA_KEY_FN KEY_F3
96 +#define TOSA_KEY_CANCEL KEY_F4
97 +#define TOSA_KEY_OFF KEY_SUSPEND
98 +#define TOSA_KEY_CENTER KEY_F5
99 +#define TOSA_KEY_REC KEY_F6
100 +#define TOSA_KEY_LIGHT KEY_F7
101 +#define TOSA_KEY_RECORD KEY_F8
102 +#define TOSA_KEY_HOME KEY_F9
103 +#define TOSA_KEY_MAIL KEY_F10
104 +#define TOSA_KEY_OK KEY_F11
105 +#define TOSA_KEY_MENU KEY_F12
106 +#define TOSA_KEY_SYNC KEY_F13
108 +#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
109 +#define KB_DISCHARGE_DELAY 10
110 +#define KB_ACTIVATE_DELAY 10
113 +static unsigned char tosakbd_keycode[NR_SCANCODES] = {
115 + 0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P, 0, 0, 0, TOSA_KEY_OFF, 0, 0, 0, 0, /*1 - 16*/
116 + KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA, 0, 0, 0, TOSA_KEY_RECORD, 0, 0, 0, 0, /*17 - 32*/
117 + KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT, 0, 0, 0, TOSA_KEY_SYNC, 0, 0, 0, 0, /*33 - 48*/
118 + KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESS, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK, KEY_LEFTSHIFT, 0 , 0,0 , 0, 0, 0, 0, /*49 - 64*/
119 + KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDER, TOSA_KEY_HOME, TOSA_KEY_REC, TOSA_KEY_LIGHT, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /*65 - 80*/
120 + KEY_TAB, KEY_SLASH, KEY_H, KEY_M, TOSA_KEY_MENU, 0, KEY_UP, 0, 0, 0, TOSA_KEY_FN, 0, 0, 0, 0, 0, /*81 - 96*/
121 + KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, /*97 - 109*/
125 + unsigned char keycode[ARRAY_SIZE(tosakbd_keycode)];
126 + struct input_dev *input;
129 + struct timer_list timer;
130 + struct timer_list hptimer;
135 + unsigned int suspended;
136 + unsigned long suspend_jiffies;
139 +/* Helper functions for reading the keyboard matrix
140 + * Note: We should really be using pxa_gpio_mode to alter GPDR but it
141 + * requires a function call per GPIO bit which is excessive
142 + * when we need to access 12 bits at once, multiple times.
143 + * These functions must be called within local_irq_save()/local_irq_restore()
146 +static inline void tosakbd_discharge_all(void)
148 + /* STROBE All HiZ */
149 + GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
150 + GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
151 + GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
152 + GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
155 +static inline void tosakbd_activate_all(void)
157 + /* STROBE ALL -> High */
158 + GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
159 + GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
160 + GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
161 + GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
163 + udelay(KB_DISCHARGE_DELAY);
166 + GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
169 +static inline void tosakbd_activate_col(int col)
172 + /* STROBE col -> High, not col -> HiZ */
173 + GPSR1 = TOSA_GPIO_STROBE_BIT(col);
174 + GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
176 + /* STROBE col -> High, not col -> HiZ */
177 + GPSR2 = TOSA_GPIO_STROBE_BIT(col);
178 + GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
182 +static inline void tosakbd_reset_col(int col)
185 + /* STROBE col -> Low */
186 + GPCR1 = TOSA_GPIO_STROBE_BIT(col);
187 + /* STROBE col -> out, not col -> HiZ */
188 + GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
190 + /* STROBE col -> Low */
191 + GPCR2 = TOSA_GPIO_STROBE_BIT(col);
192 + /* STROBE col -> out, not col -> HiZ */
193 + GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
198 + * Read the GPIOs for POWER, RECORD and SYNC
200 +static int read_port_key_status_raw(void)
205 + if ((GPLR0 & GPIO_bit(TOSA_GPIO_ON_KEY))==0)
208 + if ((GPLR0 & GPIO_bit(TOSA_GPIO_RECORD_BTN))==0)
209 + val |= KEYMASK_REC;
211 + if ((GPLR0 & GPIO_bit(TOSA_GPIO_SYNC))==0)
212 + val |= KEYMASK_SYNC;
218 + * The tosa keyboard only generates interrupts when a key is pressed.
219 + * So when a key is pressed, we enable a timer. This timer scans the
220 + * keyboard, and this is how we detect when the key is released.
223 +/* Scan the hardware keyboard and push any changes up through the input layer */
224 +static void tosakbd_scankeyboard(struct tosakbd *tosakbd_data)
226 + unsigned int row, col, rowd;
227 + unsigned long flags;
228 + unsigned int num_pressed = 0;
230 + if (tosakbd_data->suspended)
233 + spin_lock_irqsave(&tosakbd_data->lock, flags);
235 + for (col = 0; col < KB_COLS; col++) {
237 + * Discharge the output driver capacitatance
238 + * in the keyboard matrix. (Yes it is significant..)
240 + tosakbd_discharge_all();
241 + udelay(KB_DISCHARGE_DELAY);
243 + tosakbd_activate_col( col);
244 + udelay(KB_ACTIVATE_DELAY);
246 + rowd = GET_ROWS_STATUS(col);
248 + for (row = 0; row < KB_ROWS; row++) {
249 + unsigned int scancode, pressed;
250 + scancode = SCANCODE(row, col);
251 + pressed = rowd & KB_ROWMASK(row);
252 + input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
257 + tosakbd_reset_col(col);
260 + tosakbd_activate_all();
262 + rowd = read_port_key_status_raw();
264 + for (row = 0; row < 3; row++ ) {
265 + unsigned int scancode, pressed;
266 + scancode = SCANCODE(row, KB_COLS);
267 + pressed = rowd & KB_ROWMASK(row);
268 + input_report_key(tosakbd_data->input, tosakbd_data->keycode[scancode], pressed);
272 + if (pressed && (tosakbd_data->keycode[scancode] == TOSA_KEY_OFF)
273 + && time_after(jiffies, tosakbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
274 + input_event(tosakbd_data->input, EV_PWR, TOSA_KEY_OFF, 1);
275 + tosakbd_data->suspend_jiffies = jiffies;
279 + input_sync(tosakbd_data->input);
281 + /* if any keys are pressed, enable the timer */
283 + mod_timer(&tosakbd_data->timer, jiffies + SCAN_INTERVAL);
285 + spin_unlock_irqrestore(&tosakbd_data->lock, flags);
289 + * tosa keyboard interrupt handler.
291 +static irqreturn_t tosakbd_interrupt(int irq, void *dev_id)
293 + struct tosakbd *tosakbd_data = dev_id;
295 + if (!timer_pending(&tosakbd_data->timer))
297 + /** wait chattering delay **/
299 + tosakbd_scankeyboard(tosakbd_data);
302 + return IRQ_HANDLED;
306 + * tosa timer checking for released keys
308 +static void tosakbd_timer_callback(unsigned long data)
310 + struct tosakbd *tosakbd_data = (struct tosakbd *) data;
311 + tosakbd_scankeyboard(tosakbd_data);
315 + * The headphone generates an interrupt.
316 + * We debounce the switche and pass them to the input system.
319 +static irqreturn_t tosakbd_hp_isr(int irq, void *dev_id)
321 + struct tosakbd *tosakbd_data = dev_id;
323 + if (!timer_pending(&tosakbd_data->hptimer))
324 + mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
326 + return IRQ_HANDLED;
329 +static void tosakbd_hp_timer(unsigned long data)
331 + struct tosakbd *tosakbd_data = (struct tosakbd *) data;
332 + unsigned long state;
333 + unsigned long flags;
335 + state = (GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN));
336 + if (state != tosakbd_data->hp_state) {
337 + tosakbd_data->hp_count = 0;
338 + tosakbd_data->hp_state = state;
339 + } else if (tosakbd_data->hp_count < HP_STABLE_COUNT) {
340 + tosakbd_data->hp_count++;
343 + if (tosakbd_data->hp_count >= HP_STABLE_COUNT) {
344 + spin_lock_irqsave(&tosakbd_data->lock, flags);
346 + input_report_switch(tosakbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(TOSA_GPIO_EAR_IN) & GPIO_bit(TOSA_GPIO_EAR_IN)) == 0));
347 + input_sync(tosakbd_data->input);
349 + spin_unlock_irqrestore(&tosakbd_data->lock, flags);
351 + mod_timer(&tosakbd_data->hptimer, jiffies + msecs_to_jiffies(HP_SCAN_INTERVAL));
356 +static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
358 + struct tosakbd *tosakbd = platform_get_drvdata(dev);
360 + tosakbd->suspended = 1;
365 +static int tosakbd_resume(struct platform_device *dev)
367 + struct tosakbd *tosakbd = platform_get_drvdata(dev);
369 + /* Upon resume, ignore the suspend key for a short while */
370 + tosakbd->suspend_jiffies = jiffies;
371 + tosakbd->suspended = 0;
376 +#define tosakbd_suspend NULL
377 +#define tosakbd_resume NULL
380 +static int __init tosakbd_probe(struct platform_device *pdev) {
383 + struct tosakbd *tosakbd;
384 + struct input_dev *input_dev;
386 + tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
390 + input_dev = input_allocate_device();
396 + platform_set_drvdata(pdev,tosakbd);
398 + spin_lock_init(&tosakbd->lock);
400 + /* Init Keyboard rescan timer */
401 + init_timer(&tosakbd->timer);
402 + tosakbd->timer.function = tosakbd_timer_callback;
403 + tosakbd->timer.data = (unsigned long) tosakbd;
405 + /* Init Headphone Timer */
406 + init_timer(&tosakbd->hptimer);
407 + tosakbd->hptimer.function = tosakbd_hp_timer;
408 + tosakbd->hptimer.data = (unsigned long) tosakbd;
410 + tosakbd->suspend_jiffies = jiffies;
412 + tosakbd->input = input_dev;
414 + input_dev->private = tosakbd;
415 + input_dev->name = "Tosa Keyboard";
416 + input_dev->phys = "tosakbd/input0";
417 + input_dev->cdev.dev = &pdev->dev;
419 + input_dev->id.bustype = BUS_HOST;
420 + input_dev->id.vendor = 0x0001;
421 + input_dev->id.product = 0x0001;
422 + input_dev->id.version = 0x0100;
424 + input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP) | BIT(EV_PWR) | BIT(EV_SW);
425 + input_dev->keycode = tosakbd->keycode;
426 + input_dev->keycodesize = sizeof(unsigned char);
427 + input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
429 + memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd->keycode));
430 + for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
431 + set_bit(tosakbd->keycode[i], input_dev->keybit);
432 + clear_bit(0, input_dev->keybit);
433 + set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
435 + input_register_device(tosakbd->input);
437 + /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
438 + for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
439 + pxa_gpio_mode( TOSA_GPIO_KEY_SENSE(i) | GPIO_IN);
440 + if (request_irq(TOSA_IRQ_GPIO_KEY_SENSE(i), tosakbd_interrupt,
441 + SA_INTERRUPT | SA_TRIGGER_RISING, "tosakbd", tosakbd)) {
442 + printk("tosakbd: Can't get IRQ: %d !\n", i);
446 + /* Set Strobe lines as outputs - set high */
447 + for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
448 + pxa_gpio_mode( TOSA_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
451 + // Power&Rec Button
452 + pxa_gpio_mode( TOSA_GPIO_ON_KEY | GPIO_IN);
453 + pxa_gpio_mode( TOSA_GPIO_RECORD_BTN | GPIO_IN);
454 + pxa_gpio_mode( TOSA_GPIO_SYNC | GPIO_IN);
455 + pxa_gpio_mode( TOSA_GPIO_EAR_IN | GPIO_IN);
457 + if (request_irq(TOSA_IRQ_GPIO_ON_KEY, tosakbd_interrupt, SA_INTERRUPT | SA_TRIGGER_FALLING, "On key", tosakbd) ||
458 + request_irq(TOSA_IRQ_GPIO_RECORD_BTN, tosakbd_interrupt, SA_INTERRUPT | SA_TRIGGER_FALLING, "Record key", tosakbd) ||
459 + request_irq(TOSA_IRQ_GPIO_SYNC, tosakbd_interrupt, SA_INTERRUPT | SA_TRIGGER_FALLING, "Sync key", tosakbd) ||
460 + request_irq(TOSA_IRQ_GPIO_EAR_IN, tosakbd_hp_isr, SA_INTERRUPT | SA_TRIGGER_FALLING, "HP in", tosakbd)) {
461 + printk("Could not allocate KEYBD IRQ!\n");
464 + printk(KERN_INFO "input: Tosa Keyboard Registered\n");
469 +static int tosakbd_remove(struct platform_device *dev) {
472 + struct tosakbd *tosakbd = platform_get_drvdata(dev);
474 + for (i = 0; i < TOSA_KEY_SENSE_NUM; i++)
475 + free_irq(TOSA_IRQ_GPIO_KEY_SENSE(i),tosakbd);
477 + free_irq(TOSA_IRQ_GPIO_ON_KEY,tosakbd);
478 + free_irq(TOSA_IRQ_GPIO_RECORD_BTN,tosakbd);
479 + free_irq(TOSA_IRQ_GPIO_SYNC,tosakbd);
481 + del_timer_sync(&tosakbd->timer);
483 + input_unregister_device(tosakbd->input);
490 +static struct platform_driver tosakbd_driver = {
491 + .probe = tosakbd_probe,
492 + .remove = tosakbd_remove,
493 + .suspend = tosakbd_suspend,
494 + .resume = tosakbd_resume,
496 + .name = "tosa-keyboard",
500 +static int __devinit tosakbd_init(void)
502 + return platform_driver_register(&tosakbd_driver);
505 +static void __exit tosakbd_exit(void)
507 + platform_driver_unregister(&tosakbd_driver);
510 +module_init(tosakbd_init);
511 +module_exit(tosakbd_exit);
513 +MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
514 +MODULE_DESCRIPTION("Tosa Keyboard Driver");
515 +MODULE_LICENSE("GPLv2");