summaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig326
-rw-r--r--drivers/input/keyboard/Makefile29
-rw-r--r--drivers/input/keyboard/aaed2000_kbd.c186
-rw-r--r--drivers/input/keyboard/amikbd.c258
-rw-r--r--drivers/input/keyboard/atakbd.c268
-rw-r--r--drivers/input/keyboard/atkbd.c1537
-rw-r--r--drivers/input/keyboard/bf54x-keys.c421
-rw-r--r--drivers/input/keyboard/corgikbd.c416
-rw-r--r--drivers/input/keyboard/gpio_keys.c276
-rw-r--r--drivers/input/keyboard/hil_kbd.c402
-rw-r--r--drivers/input/keyboard/hilkbd.c377
-rw-r--r--drivers/input/keyboard/hpps2atkbd.h110
-rw-r--r--drivers/input/keyboard/jornada680_kbd.c279
-rw-r--r--drivers/input/keyboard/jornada720_kbd.c188
-rw-r--r--drivers/input/keyboard/lkkbd.c767
-rw-r--r--drivers/input/keyboard/locomokbd.c332
-rw-r--r--drivers/input/keyboard/maple_keyb.c253
-rw-r--r--drivers/input/keyboard/newtonkbd.c180
-rw-r--r--drivers/input/keyboard/omap-keypad.c487
-rw-r--r--drivers/input/keyboard/pxa27x_keypad.c600
-rw-r--r--drivers/input/keyboard/sh_keysc.c267
-rw-r--r--drivers/input/keyboard/spitzkbd.c498
-rw-r--r--drivers/input/keyboard/stowaway.c184
-rw-r--r--drivers/input/keyboard/sunkbd.c366
-rw-r--r--drivers/input/keyboard/tosakbd.c430
-rw-r--r--drivers/input/keyboard/xtkbd.c183
26 files changed, 9620 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
new file mode 100644
index 0000000..efd70a9
--- /dev/null
+++ b/drivers/input/keyboard/Kconfig
@@ -0,0 +1,326 @@
+#
+# Input core configuration
+#
+menuconfig INPUT_KEYBOARD
+ bool "Keyboards" if EMBEDDED || !X86
+ default y
+ help
+ Say Y here, and a list of supported keyboards will be displayed.
+ This option doesn't affect the kernel.
+
+ If unsure, say Y.
+
+if INPUT_KEYBOARD
+
+config KEYBOARD_ATKBD
+ tristate "AT keyboard" if EMBEDDED || !X86_PC
+ default y
+ select SERIO
+ select SERIO_LIBPS2
+ select SERIO_I8042 if X86_PC
+ select SERIO_GSCPS2 if GSC
+ help
+ Say Y here if you want to use a standard AT or PS/2 keyboard. Usually
+ you'll need this, unless you have a different type keyboard (USB, ADB
+ or other). This also works for AT and PS/2 keyboards connected over a
+ PS/2 to serial converter.
+
+ If unsure, say Y.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atkbd.
+
+config KEYBOARD_ATKBD_HP_KEYCODES
+ bool "Use HP keyboard scancodes"
+ depends on PARISC && KEYBOARD_ATKBD
+ default y
+ help
+ Say Y here if you have a PA-RISC machine and want to use an AT or
+ PS/2 keyboard, and your keyboard uses keycodes that are specific to
+ PA-RISC keyboards.
+
+ Say N if you use a standard keyboard.
+
+config KEYBOARD_ATKBD_RDI_KEYCODES
+ bool "Use PrecisionBook keyboard scancodes"
+ depends on KEYBOARD_ATKBD_HP_KEYCODES
+ default n
+ help
+ If you have an RDI PrecisionBook, say Y here if you want to use its
+ built-in keyboard (as opposed to an external keyboard).
+
+ The PrecisionBook has five keys that conflict with those used by most
+ AT and PS/2 keyboards. These are as follows:
+
+ PrecisionBook Standard AT or PS/2
+
+ F1 F12
+ Left Ctrl Left Alt
+ Caps Lock Left Ctrl
+ Right Ctrl Caps Lock
+ Left 102nd key (the key to the right of Left Shift)
+
+ If you say N here, and use the PrecisionBook keyboard, then each key
+ in the left-hand column will be interpreted as the corresponding key
+ in the right-hand column.
+
+ If you say Y here, and use an external keyboard, then each key in the
+ right-hand column will be interpreted as the key shown in the
+ left-hand column.
+
+config KEYBOARD_SUNKBD
+ tristate "Sun Type 4 and Type 5 keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use a Sun Type 4 or Type 5 keyboard,
+ connected either to the Sun keyboard connector or to an serial
+ (RS-232) port via a simple adapter.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sunkbd.
+
+config KEYBOARD_LKKBD
+ tristate "DECstation/VAXstation LK201/LK401 keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use a LK201 or LK401 style serial
+ keyboard. This keyboard is also useable on PCs if you attach
+ it with the inputattach program. The connector pinout is
+ described within lkkbd.c.
+
+ To compile this driver as a module, choose M here: the
+ module will be called lkkbd.
+
+config KEYBOARD_LOCOMO
+ tristate "LoCoMo Keyboard Support"
+ depends on SHARP_LOCOMO && INPUT_KEYBOARD
+ help
+ Say Y here if you are running Linux on a Sharp Zaurus Collie or Poodle based PDA
+
+ To compile this driver as a module, choose M here: the
+ module will be called locomokbd.
+
+config KEYBOARD_XTKBD
+ tristate "XT keyboard"
+ select SERIO
+ help
+ Say Y here if you want to use the old IBM PC/XT keyboard (or
+ compatible) on your system. This is only possible with a
+ parallel port keyboard adapter, you cannot connect it to the
+ keyboard port on a PC that runs Linux.
+
+ To compile this driver as a module, choose M here: the
+ module will be called xtkbd.
+
+config KEYBOARD_NEWTON
+ tristate "Newton keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Newton keyboard on a serial port.
+
+ To compile this driver as a module, choose M here: the
+ module will be called newtonkbd.
+
+config KEYBOARD_STOWAWAY
+ tristate "Stowaway keyboard"
+ select SERIO
+ help
+ Say Y here if you have a Stowaway keyboard on a serial port.
+ Stowaway compatible keyboards like Dicota Input-PDA keyboard
+ are also supported by this driver.
+
+ To compile this driver as a module, choose M here: the
+ module will be called stowaway.
+
+config KEYBOARD_CORGI
+ tristate "Corgi keyboard"
+ depends on PXA_SHARPSL
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-C7xx
+ series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called corgikbd.
+
+config KEYBOARD_SPITZ
+ tristate "Spitz keyboard"
+ depends on PXA_SHARPSL
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-C1000,
+ SL-C3000 and Sl-C3100 series of PDAs.
+
+ To compile this driver as a module, choose M here: the
+ module will be called spitzkbd.
+
+config KEYBOARD_TOSA
+ tristate "Tosa keyboard"
+ depends on MACH_TOSA
+ default y
+ help
+ Say Y here to enable the keyboard on the Sharp Zaurus SL-6000x (Tosa)
+
+ To compile this driver as a module, choose M here: the
+ module will be called tosakbd.
+
+config KEYBOARD_TOSA_USE_EXT_KEYCODES
+ bool "Tosa keyboard: use extended keycodes"
+ depends on KEYBOARD_TOSA
+ default n
+ help
+ Say Y here to enable the tosa keyboard driver to generate extended
+ (>= 127) keycodes. Be aware, that they can't be correctly interpreted
+ by either console keyboard driver or by Kdrive keybd driver.
+
+ Say Y only if you know, what you are doing!
+
+config KEYBOARD_AMIGA
+ tristate "Amiga keyboard"
+ depends on AMIGA
+ help
+ Say Y here if you are running Linux on any AMIGA and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called amikbd.
+
+config ATARI_KBD_CORE
+ bool
+
+config KEYBOARD_ATARI
+ tristate "Atari keyboard"
+ depends on ATARI
+ select ATARI_KBD_CORE
+ help
+ Say Y here if you are running Linux on any Atari and have a keyboard
+ attached.
+
+ To compile this driver as a module, choose M here: the
+ module will be called atakbd.
+
+config KEYBOARD_HIL_OLD
+ tristate "HP HIL keyboard support (simple driver)"
+ depends on GSC || HP300
+ default y
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models. This driver
+ was adapted from the one written for m68k/hp300, and implements
+ support for a keyboard attached to the HIL port, but not for
+ any other types of HIL input devices like mice or tablets.
+ However, it has been thoroughly tested and is stable.
+
+ If you want full HIL support including support for multiple
+ keyboards, mice, and tablets, you have to enable the
+ "HP System Device Controller i8042 Support" in the input/serio
+ submenu.
+
+config KEYBOARD_HIL
+ tristate "HP HIL keyboard support"
+ depends on GSC || HP300
+ default y
+ select HP_SDC
+ select HIL_MLC
+ select SERIO
+ help
+ The "Human Interface Loop" is a older, 8-channel USB-like
+ controller used in several Hewlett Packard models.
+ This driver implements support for HIL-keyboards attached
+ to your machine, so normally you should say Y here.
+
+config KEYBOARD_HP6XX
+ tristate "HP Jornada 6xx keyboard"
+ depends on SH_HP6XX
+ select INPUT_POLLDEV
+ help
+ Say Y here if you have a HP Jornada 620/660/680/690 and want to
+ support the built-in keyboard.
+
+ To compile this driver as a module, choose M here: the
+ module will be called jornada680_kbd.
+
+config KEYBOARD_HP7XX
+ tristate "HP Jornada 7xx keyboard"
+ depends on SA1100_JORNADA720_SSP && SA1100_SSP
+ help
+ Say Y here if you have a HP Jornada 710/720/728 and want to
+ support the built-in keyboard.
+
+ To compile this driver as a module, choose M here: the
+ module will be called jornada720_kbd.
+
+config KEYBOARD_OMAP
+ tristate "TI OMAP keypad support"
+ depends on (ARCH_OMAP1 || ARCH_OMAP2)
+ help
+ Say Y here if you want to use the OMAP keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called omap-keypad.
+
+config KEYBOARD_PXA27x
+ tristate "PXA27x/PXA3xx keypad support"
+ depends on PXA27x || PXA3xx
+ help
+ Enable support for PXA27x/PXA3xx keypad controller
+
+ To compile this driver as a module, choose M here: the
+ module will be called pxa27x_keypad.
+
+config KEYBOARD_AAED2000
+ tristate "AAED-2000 keyboard"
+ depends on MACH_AAED2000
+ select INPUT_POLLDEV
+ default y
+ help
+ Say Y here to enable the keyboard on the Agilent AAED-2000
+ development board.
+
+ To compile this driver as a module, choose M here: the
+ module will be called aaed2000_kbd.
+
+config KEYBOARD_GPIO
+ tristate "GPIO Buttons"
+ depends on GENERIC_GPIO
+ help
+ This driver implements support for buttons connected
+ to GPIO pins of various CPUs (and some other chips).
+
+ Say Y here if your device has buttons connected
+ directly to such GPIO pins. Your board-specific
+ setup logic must also provide a platform device,
+ with configuration data saying which GPIOs are used.
+
+ To compile this driver as a module, choose M here: the
+ module will be called gpio-keys.
+
+config KEYBOARD_MAPLE
+ tristate "Maple bus keyboard"
+ depends on SH_DREAMCAST && MAPLE
+ help
+ Say Y here if you have a Dreamcast console running Linux and have
+ a keyboard attached to its Maple bus.
+
+ To compile this driver as a module, choose M here: the
+ module will be called maple_keyb.
+
+config KEYBOARD_BFIN
+ tristate "Blackfin BF54x keypad support"
+ depends on (BF54x && !BF544)
+ help
+ Say Y here if you want to use the BF54x keypad.
+
+ To compile this driver as a module, choose M here: the
+ module will be called bf54x-keys.
+
+config KEYBOARD_SH_KEYSC
+ tristate "SuperH KEYSC keypad support"
+ depends on SUPERH
+ help
+ Say Y here if you want to use a keypad attached to the KEYSC block
+ on SuperH processors such as sh7722 and sh7343.
+
+ To compile this driver as a module, choose M here: the
+ module will be called sh_keysc.
+endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
new file mode 100644
index 0000000..0edc8f2
--- /dev/null
+++ b/drivers/input/keyboard/Makefile
@@ -0,0 +1,29 @@
+#
+# Makefile for the input core drivers.
+#
+
+# Each configuration option enables a list of files.
+
+obj-$(CONFIG_KEYBOARD_ATKBD) += atkbd.o
+obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
+obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
+obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_AMIGA) += amikbd.o
+obj-$(CONFIG_KEYBOARD_ATARI) += atakbd.o
+obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
+obj-$(CONFIG_KEYBOARD_NEWTON) += newtonkbd.o
+obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
+obj-$(CONFIG_KEYBOARD_CORGI) += corgikbd.o
+obj-$(CONFIG_KEYBOARD_SPITZ) += spitzkbd.o
+obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
+obj-$(CONFIG_KEYBOARD_HIL) += hil_kbd.o
+obj-$(CONFIG_KEYBOARD_HIL_OLD) += hilkbd.o
+obj-$(CONFIG_KEYBOARD_OMAP) += omap-keypad.o
+obj-$(CONFIG_KEYBOARD_PXA27x) += pxa27x_keypad.o
+obj-$(CONFIG_KEYBOARD_AAED2000) += aaed2000_kbd.o
+obj-$(CONFIG_KEYBOARD_GPIO) += gpio_keys.o
+obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
+obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
+obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
+obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_SH_KEYSC) += sh_keysc.o
diff --git a/drivers/input/keyboard/aaed2000_kbd.c b/drivers/input/keyboard/aaed2000_kbd.c
new file mode 100644
index 0000000..18222a6
--- /dev/null
+++ b/drivers/input/keyboard/aaed2000_kbd.c
@@ -0,0 +1,186 @@
+/*
+ * Keyboard driver for the AAED-2000 dev board
+ *
+ * Copyright (c) 2006 Nicolas Bellido Y Ortega
+ *
+ * Based on corgikbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <mach/hardware.h>
+#include <mach/aaed2000.h>
+
+#define KB_ROWS 12
+#define KB_COLS 8
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r,c) (((c) * KB_ROWS) + (r))
+#define NR_SCANCODES (KB_COLS * KB_ROWS)
+
+#define SCAN_INTERVAL (50) /* ms */
+#define KB_ACTIVATE_DELAY (20) /* us */
+
+static unsigned char aaedkbd_keycode[NR_SCANCODES] = {
+ KEY_9, KEY_0, KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, 0, KEY_SPACE, KEY_KP6, 0, KEY_KPDOT, 0, 0,
+ KEY_K, KEY_M, KEY_O, KEY_DOT, KEY_SLASH, 0, KEY_F, 0, 0, 0, KEY_LEFTSHIFT, 0,
+ KEY_I, KEY_P, KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, 0, 0, 0, 0, KEY_RIGHTSHIFT, 0,
+ KEY_8, KEY_L, KEY_SEMICOLON, KEY_APOSTROPHE, KEY_ENTER, 0, 0, 0, 0, 0, 0, 0,
+ KEY_J, KEY_H, KEY_B, KEY_KP8, KEY_KP4, 0, KEY_C, KEY_D, KEY_S, KEY_A, 0, KEY_CAPSLOCK,
+ KEY_Y, KEY_U, KEY_N, KEY_T, 0, 0, KEY_R, KEY_E, KEY_W, KEY_Q, 0, KEY_TAB,
+ KEY_7, KEY_6, KEY_G, 0, KEY_5, 0, KEY_4, KEY_3, KEY_2, KEY_1, 0, KEY_GRAVE,
+ 0, 0, KEY_COMMA, 0, KEY_KP2, 0, KEY_V, KEY_LEFTALT, KEY_X, KEY_Z, 0, KEY_LEFTCTRL
+};
+
+struct aaedkbd {
+ unsigned char keycode[ARRAY_SIZE(aaedkbd_keycode)];
+ struct input_polled_dev *poll_dev;
+ int kbdscan_state[KB_COLS];
+ int kbdscan_count[KB_COLS];
+};
+
+#define KBDSCAN_STABLE_COUNT 2
+
+static void aaedkbd_report_col(struct aaedkbd *aaedkbd,
+ unsigned int col, unsigned int rowd)
+{
+ unsigned int scancode, pressed;
+ unsigned int row;
+
+ for (row = 0; row < KB_ROWS; row++) {
+ scancode = SCANCODE(row, col);
+ pressed = rowd & KB_ROWMASK(row);
+
+ input_report_key(aaedkbd->poll_dev->input,
+ aaedkbd->keycode[scancode], pressed);
+ }
+}
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void aaedkbd_poll(struct input_polled_dev *dev)
+{
+ struct aaedkbd *aaedkbd = dev->private;
+ unsigned int col, rowd;
+
+ col = 0;
+ do {
+ AAEC_GPIO_KSCAN = col + 8;
+ udelay(KB_ACTIVATE_DELAY);
+ rowd = AAED_EXT_GPIO & AAED_EGPIO_KBD_SCAN;
+
+ if (rowd != aaedkbd->kbdscan_state[col]) {
+ aaedkbd->kbdscan_count[col] = 0;
+ aaedkbd->kbdscan_state[col] = rowd;
+ } else if (++aaedkbd->kbdscan_count[col] >= KBDSCAN_STABLE_COUNT) {
+ aaedkbd_report_col(aaedkbd, col, rowd);
+ col++;
+ }
+ } while (col < KB_COLS);
+
+ AAEC_GPIO_KSCAN = 0x07;
+ input_sync(dev->input);
+}
+
+static int __devinit aaedkbd_probe(struct platform_device *pdev)
+{
+ struct aaedkbd *aaedkbd;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input_dev;
+ int i;
+ int error;
+
+ aaedkbd = kzalloc(sizeof(struct aaedkbd), GFP_KERNEL);
+ poll_dev = input_allocate_polled_device();
+ if (!aaedkbd || !poll_dev) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ platform_set_drvdata(pdev, aaedkbd);
+
+ aaedkbd->poll_dev = poll_dev;
+ memcpy(aaedkbd->keycode, aaedkbd_keycode, sizeof(aaedkbd->keycode));
+
+ poll_dev->private = aaedkbd;
+ poll_dev->poll = aaedkbd_poll;
+ poll_dev->poll_interval = SCAN_INTERVAL;
+
+ input_dev = poll_dev->input;
+ input_dev->name = "AAED-2000 Keyboard";
+ input_dev->phys = "aaedkbd/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->keycode = aaedkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(aaedkbd_keycode);
+
+ for (i = 0; i < ARRAY_SIZE(aaedkbd_keycode); i++)
+ set_bit(aaedkbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ error = input_register_polled_device(aaedkbd->poll_dev);
+ if (error)
+ goto fail;
+
+ return 0;
+
+ fail: kfree(aaedkbd);
+ input_free_polled_device(poll_dev);
+ return error;
+}
+
+static int __devexit aaedkbd_remove(struct platform_device *pdev)
+{
+ struct aaedkbd *aaedkbd = platform_get_drvdata(pdev);
+
+ input_unregister_polled_device(aaedkbd->poll_dev);
+ input_free_polled_device(aaedkbd->poll_dev);
+ kfree(aaedkbd);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:aaed2000-keyboard");
+
+static struct platform_driver aaedkbd_driver = {
+ .probe = aaedkbd_probe,
+ .remove = __devexit_p(aaedkbd_remove),
+ .driver = {
+ .name = "aaed2000-keyboard",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init aaedkbd_init(void)
+{
+ return platform_driver_register(&aaedkbd_driver);
+}
+
+static void __exit aaedkbd_exit(void)
+{
+ platform_driver_unregister(&aaedkbd_driver);
+}
+
+module_init(aaedkbd_init);
+module_exit(aaedkbd_exit);
+
+MODULE_AUTHOR("Nicolas Bellido Y Ortega");
+MODULE_DESCRIPTION("AAED-2000 Keyboard Driver");
+MODULE_LICENSE("GPL v2");
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 0000000..35149ec
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,258 @@
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Amiga keyboard driver for Linux/m68k
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/keyboard.h>
+
+#include <asm/amigaints.h>
+#include <asm/amigahw.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION("Amiga keyboard driver");
+MODULE_LICENSE("GPL");
+
+static unsigned char amikbd_keycode[0x78] __initdata = {
+ [0] = KEY_GRAVE,
+ [1] = KEY_1,
+ [2] = KEY_2,
+ [3] = KEY_3,
+ [4] = KEY_4,
+ [5] = KEY_5,
+ [6] = KEY_6,
+ [7] = KEY_7,
+ [8] = KEY_8,
+ [9] = KEY_9,
+ [10] = KEY_0,
+ [11] = KEY_MINUS,
+ [12] = KEY_EQUAL,
+ [13] = KEY_BACKSLASH,
+ [15] = KEY_KP0,
+ [16] = KEY_Q,
+ [17] = KEY_W,
+ [18] = KEY_E,
+ [19] = KEY_R,
+ [20] = KEY_T,
+ [21] = KEY_Y,
+ [22] = KEY_U,
+ [23] = KEY_I,
+ [24] = KEY_O,
+ [25] = KEY_P,
+ [26] = KEY_LEFTBRACE,
+ [27] = KEY_RIGHTBRACE,
+ [29] = KEY_KP1,
+ [30] = KEY_KP2,
+ [31] = KEY_KP3,
+ [32] = KEY_A,
+ [33] = KEY_S,
+ [34] = KEY_D,
+ [35] = KEY_F,
+ [36] = KEY_G,
+ [37] = KEY_H,
+ [38] = KEY_J,
+ [39] = KEY_K,
+ [40] = KEY_L,
+ [41] = KEY_SEMICOLON,
+ [42] = KEY_APOSTROPHE,
+ [43] = KEY_BACKSLASH,
+ [45] = KEY_KP4,
+ [46] = KEY_KP5,
+ [47] = KEY_KP6,
+ [48] = KEY_102ND,
+ [49] = KEY_Z,
+ [50] = KEY_X,
+ [51] = KEY_C,
+ [52] = KEY_V,
+ [53] = KEY_B,
+ [54] = KEY_N,
+ [55] = KEY_M,
+ [56] = KEY_COMMA,
+ [57] = KEY_DOT,
+ [58] = KEY_SLASH,
+ [60] = KEY_KPDOT,
+ [61] = KEY_KP7,
+ [62] = KEY_KP8,
+ [63] = KEY_KP9,
+ [64] = KEY_SPACE,
+ [65] = KEY_BACKSPACE,
+ [66] = KEY_TAB,
+ [67] = KEY_KPENTER,
+ [68] = KEY_ENTER,
+ [69] = KEY_ESC,
+ [70] = KEY_DELETE,
+ [74] = KEY_KPMINUS,
+ [76] = KEY_UP,
+ [77] = KEY_DOWN,
+ [78] = KEY_RIGHT,
+ [79] = KEY_LEFT,
+ [80] = KEY_F1,
+ [81] = KEY_F2,
+ [82] = KEY_F3,
+ [83] = KEY_F4,
+ [84] = KEY_F5,
+ [85] = KEY_F6,
+ [86] = KEY_F7,
+ [87] = KEY_F8,
+ [88] = KEY_F9,
+ [89] = KEY_F10,
+ [90] = KEY_KPLEFTPAREN,
+ [91] = KEY_KPRIGHTPAREN,
+ [92] = KEY_KPSLASH,
+ [93] = KEY_KPASTERISK,
+ [94] = KEY_KPPLUS,
+ [95] = KEY_HELP,
+ [96] = KEY_LEFTSHIFT,
+ [97] = KEY_RIGHTSHIFT,
+ [98] = KEY_CAPSLOCK,
+ [99] = KEY_LEFTCTRL,
+ [100] = KEY_LEFTALT,
+ [101] = KEY_RIGHTALT,
+ [102] = KEY_LEFTMETA,
+ [103] = KEY_RIGHTMETA
+};
+
+static const char *amikbd_messages[8] = {
+ [0] = KERN_ALERT "amikbd: Ctrl-Amiga-Amiga reset warning!!\n",
+ [1] = KERN_WARNING "amikbd: keyboard lost sync\n",
+ [2] = KERN_WARNING "amikbd: keyboard buffer overflow\n",
+ [3] = KERN_WARNING "amikbd: keyboard controller failure\n",
+ [4] = KERN_ERR "amikbd: keyboard selftest failure\n",
+ [5] = KERN_INFO "amikbd: initiate power-up key stream\n",
+ [6] = KERN_INFO "amikbd: terminate power-up key stream\n",
+ [7] = KERN_WARNING "amikbd: keyboard interrupt\n"
+};
+
+static struct input_dev *amikbd_dev;
+
+static irqreturn_t amikbd_interrupt(int irq, void *dummy)
+{
+ unsigned char scancode, down;
+
+ scancode = ~ciaa.sdr; /* get and invert scancode (keyboard is active low) */
+ ciaa.cra |= 0x40; /* switch SP pin to output for handshake */
+ udelay(85); /* wait until 85 us have expired */
+ ciaa.cra &= ~0x40; /* switch CIA serial port to input mode */
+
+ down = !(scancode & 1); /* lowest bit is release bit */
+ scancode >>= 1;
+
+ if (scancode < 0x78) { /* scancodes < 0x78 are keys */
+ if (scancode == 98) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(amikbd_dev, scancode, 1);
+ input_report_key(amikbd_dev, scancode, 0);
+ } else {
+ input_report_key(amikbd_dev, scancode, down);
+ }
+
+ input_sync(amikbd_dev);
+ } else /* scancodes >= 0x78 are error codes */
+ printk(amikbd_messages[scancode - 0x78]);
+
+ return IRQ_HANDLED;
+}
+
+static int __init amikbd_init(void)
+{
+ int i, j, err;
+
+ if (!AMIGAHW_PRESENT(AMI_KEYBOARD))
+ return -ENODEV;
+
+ if (!request_mem_region(CIAA_PHYSADDR-1+0xb00, 0x100, "amikeyb"))
+ return -EBUSY;
+
+ amikbd_dev = input_allocate_device();
+ if (!amikbd_dev) {
+ printk(KERN_ERR "amikbd: not enough memory for input device\n");
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ amikbd_dev->name = "Amiga Keyboard";
+ amikbd_dev->phys = "amikbd/input0";
+ amikbd_dev->id.bustype = BUS_AMIGA;
+ amikbd_dev->id.vendor = 0x0001;
+ amikbd_dev->id.product = 0x0001;
+ amikbd_dev->id.version = 0x0100;
+
+ amikbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+
+ for (i = 0; i < 0x78; i++)
+ set_bit(i, amikbd_dev->keybit);
+
+ for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+ static u_short temp_map[NR_KEYS] __initdata;
+ if (!key_maps[i])
+ continue;
+ memset(temp_map, 0, sizeof(temp_map));
+ for (j = 0; j < 0x78; j++) {
+ if (!amikbd_keycode[j])
+ continue;
+ temp_map[j] = key_maps[i][amikbd_keycode[j]];
+ }
+ for (j = 0; j < NR_KEYS; j++) {
+ if (!temp_map[j])
+ temp_map[j] = 0xf200;
+ }
+ memcpy(key_maps[i], temp_map, sizeof(temp_map));
+ }
+ ciaa.cra &= ~0x41; /* serial data in, turn off TA */
+ if (request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
+ amikbd_interrupt)) {
+ err = -EBUSY;
+ goto fail2;
+ }
+
+ err = input_register_device(amikbd_dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
+ fail2: input_free_device(amikbd_dev);
+ fail1: release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
+ return err;
+}
+
+static void __exit amikbd_exit(void)
+{
+ free_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt);
+ input_unregister_device(amikbd_dev);
+ release_mem_region(CIAA_PHYSADDR - 1 + 0xb00, 0x100);
+}
+
+module_init(amikbd_init);
+module_exit(amikbd_exit);
diff --git a/drivers/input/keyboard/atakbd.c b/drivers/input/keyboard/atakbd.c
new file mode 100644
index 0000000..1839194
--- /dev/null
+++ b/drivers/input/keyboard/atakbd.c
@@ -0,0 +1,268 @@
+/*
+ * atakbd.c
+ *
+ * Copyright (c) 2005 Michael Schmitz
+ *
+ * Based on amikbd.c, which is
+ *
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Atari keyboard driver for Linux/m68k
+ *
+ * The low level init and interrupt stuff is handled in arch/mm68k/atari/atakeyb.c
+ * (the keyboard ACIA also handles the mouse and joystick data, and the keyboard
+ * interrupt is shared with the MIDI ACIA so MIDI data also get handled there).
+ * This driver only deals with handing key events off to the input layer.
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <asm/atariints.h>
+#include <asm/atarihw.h>
+#include <asm/atarikb.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("Michael Schmitz <schmitz@biophys.uni-duesseldorf.de>");
+MODULE_DESCRIPTION("Atari keyboard driver");
+MODULE_LICENSE("GPL");
+
+/*
+ 0x47: KP_7 71
+ 0x48: KP_8 72
+ 0x49: KP_9 73
+ 0x62: KP_/ 98
+ 0x4b: KP_4 75
+ 0x4c: KP_5 76
+ 0x4d: KP_6 77
+ 0x37: KP_* 55
+ 0x4f: KP_1 79
+ 0x50: KP_2 80
+ 0x51: KP_3 81
+ 0x4a: KP_- 74
+ 0x52: KP_0 82
+ 0x53: KP_. 83
+ 0x4e: KP_+ 78
+
+ 0x67: Up 103
+ 0x6c: Down 108
+ 0x69: Left 105
+ 0x6a: Right 106
+ */
+
+
+static unsigned char atakbd_keycode[0x72] = { /* American layout */
+ [0] = KEY_GRAVE,
+ [1] = KEY_ESC,
+ [2] = KEY_1,
+ [3] = KEY_2,
+ [4] = KEY_3,
+ [5] = KEY_4,
+ [6] = KEY_5,
+ [7] = KEY_6,
+ [8] = KEY_7,
+ [9] = KEY_8,
+ [10] = KEY_9,
+ [11] = KEY_0,
+ [12] = KEY_MINUS,
+ [13] = KEY_EQUAL,
+ [14] = KEY_BACKSPACE,
+ [15] = KEY_TAB,
+ [16] = KEY_Q,
+ [17] = KEY_W,
+ [18] = KEY_E,
+ [19] = KEY_R,
+ [20] = KEY_T,
+ [21] = KEY_Y,
+ [22] = KEY_U,
+ [23] = KEY_I,
+ [24] = KEY_O,
+ [25] = KEY_P,
+ [26] = KEY_LEFTBRACE,
+ [27] = KEY_RIGHTBRACE,
+ [28] = KEY_ENTER,
+ [29] = KEY_LEFTCTRL,
+ [30] = KEY_A,
+ [31] = KEY_S,
+ [32] = KEY_D,
+ [33] = KEY_F,
+ [34] = KEY_G,
+ [35] = KEY_H,
+ [36] = KEY_J,
+ [37] = KEY_K,
+ [38] = KEY_L,
+ [39] = KEY_SEMICOLON,
+ [40] = KEY_APOSTROPHE,
+ [41] = KEY_BACKSLASH, /* FIXME, '#' */
+ [42] = KEY_LEFTSHIFT,
+ [43] = KEY_GRAVE, /* FIXME: '~' */
+ [44] = KEY_Z,
+ [45] = KEY_X,
+ [46] = KEY_C,
+ [47] = KEY_V,
+ [48] = KEY_B,
+ [49] = KEY_N,
+ [50] = KEY_M,
+ [51] = KEY_COMMA,
+ [52] = KEY_DOT,
+ [53] = KEY_SLASH,
+ [54] = KEY_RIGHTSHIFT,
+ [55] = KEY_KPASTERISK,
+ [56] = KEY_LEFTALT,
+ [57] = KEY_SPACE,
+ [58] = KEY_CAPSLOCK,
+ [59] = KEY_F1,
+ [60] = KEY_F2,
+ [61] = KEY_F3,
+ [62] = KEY_F4,
+ [63] = KEY_F5,
+ [64] = KEY_F6,
+ [65] = KEY_F7,
+ [66] = KEY_F8,
+ [67] = KEY_F9,
+ [68] = KEY_F10,
+ [69] = KEY_ESC,
+ [70] = KEY_DELETE,
+ [71] = KEY_KP7,
+ [72] = KEY_KP8,
+ [73] = KEY_KP9,
+ [74] = KEY_KPMINUS,
+ [75] = KEY_KP4,
+ [76] = KEY_KP5,
+ [77] = KEY_KP6,
+ [78] = KEY_KPPLUS,
+ [79] = KEY_KP1,
+ [80] = KEY_KP2,
+ [81] = KEY_KP3,
+ [82] = KEY_KP0,
+ [83] = KEY_KPDOT,
+ [90] = KEY_KPLEFTPAREN,
+ [91] = KEY_KPRIGHTPAREN,
+ [92] = KEY_KPASTERISK, /* FIXME */
+ [93] = KEY_KPASTERISK,
+ [94] = KEY_KPPLUS,
+ [95] = KEY_HELP,
+ [96] = KEY_BACKSLASH, /* FIXME: '<' */
+ [97] = KEY_KPASTERISK, /* FIXME */
+ [98] = KEY_KPSLASH,
+ [99] = KEY_KPLEFTPAREN,
+ [100] = KEY_KPRIGHTPAREN,
+ [101] = KEY_KPSLASH,
+ [102] = KEY_KPASTERISK,
+ [103] = KEY_UP,
+ [104] = KEY_KPASTERISK, /* FIXME */
+ [105] = KEY_LEFT,
+ [106] = KEY_RIGHT,
+ [107] = KEY_KPASTERISK, /* FIXME */
+ [108] = KEY_DOWN,
+ [109] = KEY_KPASTERISK, /* FIXME */
+ [110] = KEY_KPASTERISK, /* FIXME */
+ [111] = KEY_KPASTERISK, /* FIXME */
+ [112] = KEY_KPASTERISK, /* FIXME */
+ [113] = KEY_KPASTERISK /* FIXME */
+};
+
+static struct input_dev *atakbd_dev;
+
+static void atakbd_interrupt(unsigned char scancode, char down)
+{
+
+ if (scancode < 0x72) { /* scancodes < 0xf2 are keys */
+
+ // report raw events here?
+
+ scancode = atakbd_keycode[scancode];
+
+ if (scancode == KEY_CAPSLOCK) { /* CapsLock is a toggle switch key on Amiga */
+ input_report_key(atakbd_dev, scancode, 1);
+ input_report_key(atakbd_dev, scancode, 0);
+ input_sync(atakbd_dev);
+ } else {
+ input_report_key(atakbd_dev, scancode, down);
+ input_sync(atakbd_dev);
+ }
+ } else /* scancodes >= 0xf2 are mouse data, most likely */
+ printk(KERN_INFO "atakbd: unhandled scancode %x\n", scancode);
+
+ return;
+}
+
+static int __init atakbd_init(void)
+{
+ int i, error;
+
+ if (!MACH_IS_ATARI || !ATARIHW_PRESENT(ST_MFP))
+ return -ENODEV;
+
+ // need to init core driver if not already done so
+ if (atari_keyb_init())
+ return -ENODEV;
+
+ atakbd_dev = input_allocate_device();
+ if (!atakbd_dev)
+ return -ENOMEM;
+
+ atakbd_dev->name = "Atari Keyboard";
+ atakbd_dev->phys = "atakbd/input0";
+ atakbd_dev->id.bustype = BUS_HOST;
+ atakbd_dev->id.vendor = 0x0001;
+ atakbd_dev->id.product = 0x0001;
+ atakbd_dev->id.version = 0x0100;
+
+ atakbd_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ atakbd_dev->keycode = atakbd_keycode;
+ atakbd_dev->keycodesize = sizeof(unsigned char);
+ atakbd_dev->keycodemax = ARRAY_SIZE(atakbd_keycode);
+
+ for (i = 1; i < 0x72; i++) {
+ set_bit(atakbd_keycode[i], atakbd_dev->keybit);
+ }
+
+ /* error check */
+ error = input_register_device(atakbd_dev);
+ if (error) {
+ input_free_device(atakbd_dev);
+ return error;
+ }
+
+ atari_input_keyboard_interrupt_hook = atakbd_interrupt;
+
+ return 0;
+}
+
+static void __exit atakbd_exit(void)
+{
+ atari_input_keyboard_interrupt_hook = NULL;
+ input_unregister_device(atakbd_dev);
+}
+
+module_init(atakbd_init);
+module_exit(atakbd_exit);
diff --git a/drivers/input/keyboard/atkbd.c b/drivers/input/keyboard/atkbd.c
new file mode 100644
index 0000000..159dbb6
--- /dev/null
+++ b/drivers/input/keyboard/atkbd.c
@@ -0,0 +1,1537 @@
+/*
+ * AT and PS/2 keyboard driver
+ *
+ * Copyright (c) 1999-2002 Vojtech Pavlik
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ */
+
+/*
+ * This driver can handle standard AT keyboards and PS/2 keyboards in
+ * Translated and Raw Set 2 and Set 3, as well as AT keyboards on dumb
+ * input-only controllers and AT keyboards connected over a one way RS232
+ * converter.
+ */
+
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+#include <linux/libps2.h>
+#include <linux/mutex.h>
+#include <linux/dmi.h>
+
+#define DRIVER_DESC "AT and PS/2 keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@suse.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static int atkbd_set = 2;
+module_param_named(set, atkbd_set, int, 0);
+MODULE_PARM_DESC(set, "Select keyboard code set (2 = default, 3 = PS/2 native)");
+
+#if defined(__i386__) || defined(__x86_64__) || defined(__hppa__)
+static int atkbd_reset;
+#else
+static int atkbd_reset = 1;
+#endif
+module_param_named(reset, atkbd_reset, bool, 0);
+MODULE_PARM_DESC(reset, "Reset keyboard during initialization");
+
+static int atkbd_softrepeat;
+module_param_named(softrepeat, atkbd_softrepeat, bool, 0);
+MODULE_PARM_DESC(softrepeat, "Use software keyboard repeat");
+
+static int atkbd_softraw = 1;
+module_param_named(softraw, atkbd_softraw, bool, 0);
+MODULE_PARM_DESC(softraw, "Use software generated rawmode");
+
+static int atkbd_scroll;
+module_param_named(scroll, atkbd_scroll, bool, 0);
+MODULE_PARM_DESC(scroll, "Enable scroll-wheel on MS Office and similar keyboards");
+
+static int atkbd_extra;
+module_param_named(extra, atkbd_extra, bool, 0);
+MODULE_PARM_DESC(extra, "Enable extra LEDs and keys on IBM RapidAcces, EzKey and similar keyboards");
+
+/*
+ * Scancode to keycode tables. These are just the default setting, and
+ * are loadable via an userland utility.
+ */
+
+static const unsigned short atkbd_set2_keycode[512] = {
+
+#ifdef CONFIG_KEYBOARD_ATKBD_HP_KEYCODES
+
+/* XXX: need a more general approach */
+
+#include "hpps2atkbd.h" /* include the keyboard scancodes */
+
+#else
+ 0, 67, 65, 63, 61, 59, 60, 88, 0, 68, 66, 64, 62, 15, 41,117,
+ 0, 56, 42, 93, 29, 16, 2, 0, 0, 0, 44, 31, 30, 17, 3, 0,
+ 0, 46, 45, 32, 18, 5, 4, 95, 0, 57, 47, 33, 20, 19, 6,183,
+ 0, 49, 48, 35, 34, 21, 7,184, 0, 0, 50, 36, 22, 8, 9,185,
+ 0, 51, 37, 23, 24, 11, 10, 0, 0, 52, 53, 38, 39, 25, 12, 0,
+ 0, 89, 40, 0, 26, 13, 0, 0, 58, 54, 28, 27, 0, 43, 0, 85,
+ 0, 86, 91, 90, 92, 0, 14, 94, 0, 79,124, 75, 71,121, 0, 0,
+ 82, 83, 80, 76, 77, 72, 1, 69, 87, 78, 81, 74, 55, 73, 70, 99,
+
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 217,100,255, 0, 97,165, 0, 0,156, 0, 0, 0, 0, 0, 0,125,
+ 173,114, 0,113, 0, 0, 0,126,128, 0, 0,140, 0, 0, 0,127,
+ 159, 0,115, 0,164, 0, 0,116,158, 0,172,166, 0, 0, 0,142,
+ 157, 0, 0, 0, 0, 0, 0, 0,155, 0, 98, 0, 0,163, 0, 0,
+ 226, 0, 0, 0, 0, 0, 0, 0, 0,255, 96, 0, 0, 0,143, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0,107, 0,105,102, 0, 0,112,
+ 110,111,108,112,106,103, 0,119, 0,118,109, 0, 99,104,119, 0,
+
+ 0, 0, 0, 65, 99,
+#endif
+};
+
+static const unsigned short atkbd_set3_keycode[512] = {
+
+ 0, 0, 0, 0, 0, 0, 0, 59, 1,138,128,129,130, 15, 41, 60,
+ 131, 29, 42, 86, 58, 16, 2, 61,133, 56, 44, 31, 30, 17, 3, 62,
+ 134, 46, 45, 32, 18, 5, 4, 63,135, 57, 47, 33, 20, 19, 6, 64,
+ 136, 49, 48, 35, 34, 21, 7, 65,137,100, 50, 36, 22, 8, 9, 66,
+ 125, 51, 37, 23, 24, 11, 10, 67,126, 52, 53, 38, 39, 25, 12, 68,
+ 113,114, 40, 43, 26, 13, 87, 99, 97, 54, 28, 27, 43, 43, 88, 70,
+ 108,105,119,103,111,107, 14,110, 0, 79,106, 75, 71,109,102,104,
+ 82, 83, 80, 76, 77, 72, 69, 98, 0, 96, 81, 0, 78, 73, 55,183,
+
+ 184,185,186,187, 74, 94, 92, 93, 0, 0, 0,125,126,127,112, 0,
+ 0,139,172,163,165,115,152,172,166,140,160,154,113,114,167,168,
+ 148,149,147,140
+};
+
+static const unsigned short atkbd_unxlate_table[128] = {
+ 0,118, 22, 30, 38, 37, 46, 54, 61, 62, 70, 69, 78, 85,102, 13,
+ 21, 29, 36, 45, 44, 53, 60, 67, 68, 77, 84, 91, 90, 20, 28, 27,
+ 35, 43, 52, 51, 59, 66, 75, 76, 82, 14, 18, 93, 26, 34, 33, 42,
+ 50, 49, 58, 65, 73, 74, 89,124, 17, 41, 88, 5, 6, 4, 12, 3,
+ 11, 2, 10, 1, 9,119,126,108,117,125,123,107,115,116,121,105,
+ 114,122,112,113,127, 96, 97,120, 7, 15, 23, 31, 39, 47, 55, 63,
+ 71, 79, 86, 94, 8, 16, 24, 32, 40, 48, 56, 64, 72, 80, 87,111,
+ 19, 25, 57, 81, 83, 92, 95, 98, 99,100,101,103,104,106,109,110
+};
+
+#define ATKBD_CMD_SETLEDS 0x10ed
+#define ATKBD_CMD_GSCANSET 0x11f0
+#define ATKBD_CMD_SSCANSET 0x10f0
+#define ATKBD_CMD_GETID 0x02f2
+#define ATKBD_CMD_SETREP 0x10f3
+#define ATKBD_CMD_ENABLE 0x00f4
+#define ATKBD_CMD_RESET_DIS 0x00f5
+#define ATKBD_CMD_SETALL_MBR 0x00fa
+#define ATKBD_CMD_RESET_BAT 0x02ff
+#define ATKBD_CMD_RESEND 0x00fe
+#define ATKBD_CMD_EX_ENABLE 0x10ea
+#define ATKBD_CMD_EX_SETLEDS 0x20eb
+#define ATKBD_CMD_OK_GETID 0x02e8
+
+#define ATKBD_RET_ACK 0xfa
+#define ATKBD_RET_NAK 0xfe
+#define ATKBD_RET_BAT 0xaa
+#define ATKBD_RET_EMUL0 0xe0
+#define ATKBD_RET_EMUL1 0xe1
+#define ATKBD_RET_RELEASE 0xf0
+#define ATKBD_RET_HANJA 0xf1
+#define ATKBD_RET_HANGEUL 0xf2
+#define ATKBD_RET_ERR 0xff
+
+#define ATKBD_KEY_UNKNOWN 0
+#define ATKBD_KEY_NULL 255
+
+#define ATKBD_SCR_1 254
+#define ATKBD_SCR_2 253
+#define ATKBD_SCR_4 252
+#define ATKBD_SCR_8 251
+#define ATKBD_SCR_CLICK 250
+#define ATKBD_SCR_LEFT 249
+#define ATKBD_SCR_RIGHT 248
+
+#define ATKBD_SPECIAL ATKBD_SCR_RIGHT
+
+#define ATKBD_LED_EVENT_BIT 0
+#define ATKBD_REP_EVENT_BIT 1
+
+#define ATKBD_XL_ERR 0x01
+#define ATKBD_XL_BAT 0x02
+#define ATKBD_XL_ACK 0x04
+#define ATKBD_XL_NAK 0x08
+#define ATKBD_XL_HANGEUL 0x10
+#define ATKBD_XL_HANJA 0x20
+
+static const struct {
+ unsigned char keycode;
+ unsigned char set2;
+} atkbd_scroll_keys[] = {
+ { ATKBD_SCR_1, 0xc5 },
+ { ATKBD_SCR_2, 0x9d },
+ { ATKBD_SCR_4, 0xa4 },
+ { ATKBD_SCR_8, 0x9b },
+ { ATKBD_SCR_CLICK, 0xe0 },
+ { ATKBD_SCR_LEFT, 0xcb },
+ { ATKBD_SCR_RIGHT, 0xd2 },
+};
+
+/*
+ * The atkbd control structure
+ */
+
+struct atkbd {
+
+ struct ps2dev ps2dev;
+ struct input_dev *dev;
+
+ /* Written only during init */
+ char name[64];
+ char phys[32];
+
+ unsigned short id;
+ unsigned short keycode[512];
+ DECLARE_BITMAP(force_release_mask, 512);
+ unsigned char set;
+ unsigned char translated;
+ unsigned char extra;
+ unsigned char write;
+ unsigned char softrepeat;
+ unsigned char softraw;
+ unsigned char scroll;
+ unsigned char enabled;
+
+ /* Accessed only from interrupt */
+ unsigned char emul;
+ unsigned char resend;
+ unsigned char release;
+ unsigned long xl_bit;
+ unsigned int last;
+ unsigned long time;
+ unsigned long err_count;
+
+ struct delayed_work event_work;
+ unsigned long event_jiffies;
+ struct mutex event_mutex;
+ unsigned long event_mask;
+};
+
+/*
+ * System-specific ketymap fixup routine
+ */
+static void (*atkbd_platform_fixup)(struct atkbd *);
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *));
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t));
+#define ATKBD_DEFINE_ATTR(_name) \
+static ssize_t atkbd_show_##_name(struct atkbd *, char *); \
+static ssize_t atkbd_set_##_name(struct atkbd *, const char *, size_t); \
+static ssize_t atkbd_do_show_##_name(struct device *d, \
+ struct device_attribute *attr, char *b) \
+{ \
+ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \
+} \
+static ssize_t atkbd_do_set_##_name(struct device *d, \
+ struct device_attribute *attr, const char *b, size_t s) \
+{ \
+ return atkbd_attr_set_helper(d, b, s, atkbd_set_##_name); \
+} \
+static struct device_attribute atkbd_attr_##_name = \
+ __ATTR(_name, S_IWUSR | S_IRUGO, atkbd_do_show_##_name, atkbd_do_set_##_name);
+
+ATKBD_DEFINE_ATTR(extra);
+ATKBD_DEFINE_ATTR(scroll);
+ATKBD_DEFINE_ATTR(set);
+ATKBD_DEFINE_ATTR(softrepeat);
+ATKBD_DEFINE_ATTR(softraw);
+
+#define ATKBD_DEFINE_RO_ATTR(_name) \
+static ssize_t atkbd_show_##_name(struct atkbd *, char *); \
+static ssize_t atkbd_do_show_##_name(struct device *d, \
+ struct device_attribute *attr, char *b) \
+{ \
+ return atkbd_attr_show_helper(d, b, atkbd_show_##_name); \
+} \
+static struct device_attribute atkbd_attr_##_name = \
+ __ATTR(_name, S_IRUGO, atkbd_do_show_##_name, NULL);
+
+ATKBD_DEFINE_RO_ATTR(err_count);
+
+static struct attribute *atkbd_attributes[] = {
+ &atkbd_attr_extra.attr,
+ &atkbd_attr_scroll.attr,
+ &atkbd_attr_set.attr,
+ &atkbd_attr_softrepeat.attr,
+ &atkbd_attr_softraw.attr,
+ &atkbd_attr_err_count.attr,
+ NULL
+};
+
+static struct attribute_group atkbd_attribute_group = {
+ .attrs = atkbd_attributes,
+};
+
+static const unsigned int xl_table[] = {
+ ATKBD_RET_BAT, ATKBD_RET_ERR, ATKBD_RET_ACK,
+ ATKBD_RET_NAK, ATKBD_RET_HANJA, ATKBD_RET_HANGEUL,
+};
+
+/*
+ * Checks if we should mangle the scancode to extract 'release' bit
+ * in translated mode.
+ */
+static int atkbd_need_xlate(unsigned long xl_bit, unsigned char code)
+{
+ int i;
+
+ if (code == ATKBD_RET_EMUL0 || code == ATKBD_RET_EMUL1)
+ return 0;
+
+ for (i = 0; i < ARRAY_SIZE(xl_table); i++)
+ if (code == xl_table[i])
+ return test_bit(i, &xl_bit);
+
+ return 1;
+}
+
+/*
+ * Calculates new value of xl_bit so the driver can distinguish
+ * between make/break pair of scancodes for select keys and PS/2
+ * protocol responses.
+ */
+static void atkbd_calculate_xl_bit(struct atkbd *atkbd, unsigned char code)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(xl_table); i++) {
+ if (!((code ^ xl_table[i]) & 0x7f)) {
+ if (code & 0x80)
+ __clear_bit(i, &atkbd->xl_bit);
+ else
+ __set_bit(i, &atkbd->xl_bit);
+ break;
+ }
+ }
+}
+
+/*
+ * Encode the scancode, 0xe0 prefix, and high bit into a single integer,
+ * keeping kernel 2.4 compatibility for set 2
+ */
+static unsigned int atkbd_compat_scancode(struct atkbd *atkbd, unsigned int code)
+{
+ if (atkbd->set == 3) {
+ if (atkbd->emul == 1)
+ code |= 0x100;
+ } else {
+ code = (code & 0x7f) | ((code & 0x80) << 1);
+ if (atkbd->emul == 1)
+ code |= 0x80;
+ }
+
+ return code;
+}
+
+/*
+ * atkbd_interrupt(). Here takes place processing of data received from
+ * the keyboard into events.
+ */
+
+static irqreturn_t atkbd_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ struct input_dev *dev = atkbd->dev;
+ unsigned int code = data;
+ int scroll = 0, hscroll = 0, click = -1;
+ int value;
+ unsigned short keycode;
+
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Received %02x flags %02x\n", data, flags);
+#endif
+
+#if !defined(__i386__) && !defined (__x86_64__)
+ if ((flags & (SERIO_FRAME | SERIO_PARITY)) && (~flags & SERIO_TIMEOUT) && !atkbd->resend && atkbd->write) {
+ printk(KERN_WARNING "atkbd.c: frame/parity error: %02x\n", flags);
+ serio_write(serio, ATKBD_CMD_RESEND);
+ atkbd->resend = 1;
+ goto out;
+ }
+
+ if (!flags && data == ATKBD_RET_ACK)
+ atkbd->resend = 0;
+#endif
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_ACK))
+ if (ps2_handle_ack(&atkbd->ps2dev, data))
+ goto out;
+
+ if (unlikely(atkbd->ps2dev.flags & PS2_FLAG_CMD))
+ if (ps2_handle_response(&atkbd->ps2dev, data))
+ goto out;
+
+ if (!atkbd->enabled)
+ goto out;
+
+ input_event(dev, EV_MSC, MSC_RAW, code);
+
+ if (atkbd->translated) {
+
+ if (atkbd->emul || atkbd_need_xlate(atkbd->xl_bit, code)) {
+ atkbd->release = code >> 7;
+ code &= 0x7f;
+ }
+
+ if (!atkbd->emul)
+ atkbd_calculate_xl_bit(atkbd, data);
+ }
+
+ switch (code) {
+ case ATKBD_RET_BAT:
+ atkbd->enabled = 0;
+ serio_reconnect(atkbd->ps2dev.serio);
+ goto out;
+ case ATKBD_RET_EMUL0:
+ atkbd->emul = 1;
+ goto out;
+ case ATKBD_RET_EMUL1:
+ atkbd->emul = 2;
+ goto out;
+ case ATKBD_RET_RELEASE:
+ atkbd->release = 1;
+ goto out;
+ case ATKBD_RET_ACK:
+ case ATKBD_RET_NAK:
+ if (printk_ratelimit())
+ printk(KERN_WARNING "atkbd.c: Spurious %s on %s. "
+ "Some program might be trying access hardware directly.\n",
+ data == ATKBD_RET_ACK ? "ACK" : "NAK", serio->phys);
+ goto out;
+ case ATKBD_RET_ERR:
+ atkbd->err_count++;
+#ifdef ATKBD_DEBUG
+ printk(KERN_DEBUG "atkbd.c: Keyboard on %s reports too many keys pressed.\n", serio->phys);
+#endif
+ goto out;
+ }
+
+ code = atkbd_compat_scancode(atkbd, code);
+
+ if (atkbd->emul && --atkbd->emul)
+ goto out;
+
+ keycode = atkbd->keycode[code];
+
+ if (keycode != ATKBD_KEY_NULL)
+ input_event(dev, EV_MSC, MSC_SCAN, code);
+
+ switch (keycode) {
+ case ATKBD_KEY_NULL:
+ break;
+ case ATKBD_KEY_UNKNOWN:
+ printk(KERN_WARNING
+ "atkbd.c: Unknown key %s (%s set %d, code %#x on %s).\n",
+ atkbd->release ? "released" : "pressed",
+ atkbd->translated ? "translated" : "raw",
+ atkbd->set, code, serio->phys);
+ printk(KERN_WARNING
+ "atkbd.c: Use 'setkeycodes %s%02x <keycode>' to make it known.\n",
+ code & 0x80 ? "e0" : "", code & 0x7f);
+ input_sync(dev);
+ break;
+ case ATKBD_SCR_1:
+ scroll = 1 - atkbd->release * 2;
+ break;
+ case ATKBD_SCR_2:
+ scroll = 2 - atkbd->release * 4;
+ break;
+ case ATKBD_SCR_4:
+ scroll = 4 - atkbd->release * 8;
+ break;
+ case ATKBD_SCR_8:
+ scroll = 8 - atkbd->release * 16;
+ break;
+ case ATKBD_SCR_CLICK:
+ click = !atkbd->release;
+ break;
+ case ATKBD_SCR_LEFT:
+ hscroll = -1;
+ break;
+ case ATKBD_SCR_RIGHT:
+ hscroll = 1;
+ break;
+ default:
+ if (atkbd->release) {
+ value = 0;
+ atkbd->last = 0;
+ } else if (!atkbd->softrepeat && test_bit(keycode, dev->key)) {
+ /* Workaround Toshiba laptop multiple keypress */
+ value = time_before(jiffies, atkbd->time) && atkbd->last == code ? 1 : 2;
+ } else {
+ value = 1;
+ atkbd->last = code;
+ atkbd->time = jiffies + msecs_to_jiffies(dev->rep[REP_DELAY]) / 2;
+ }
+
+ input_event(dev, EV_KEY, keycode, value);
+ input_sync(dev);
+
+ if (value && test_bit(code, atkbd->force_release_mask)) {
+ input_report_key(dev, keycode, 0);
+ input_sync(dev);
+ }
+ }
+
+ if (atkbd->scroll) {
+ if (click != -1)
+ input_report_key(dev, BTN_MIDDLE, click);
+ input_report_rel(dev, REL_WHEEL, scroll);
+ input_report_rel(dev, REL_HWHEEL, hscroll);
+ input_sync(dev);
+ }
+
+ atkbd->release = 0;
+out:
+ return IRQ_HANDLED;
+}
+
+static int atkbd_set_repeat_rate(struct atkbd *atkbd)
+{
+ const short period[32] =
+ { 33, 37, 42, 46, 50, 54, 58, 63, 67, 75, 83, 92, 100, 109, 116, 125,
+ 133, 149, 167, 182, 200, 217, 232, 250, 270, 303, 333, 370, 400, 435, 470, 500 };
+ const short delay[4] =
+ { 250, 500, 750, 1000 };
+
+ struct input_dev *dev = atkbd->dev;
+ unsigned char param;
+ int i = 0, j = 0;
+
+ while (i < ARRAY_SIZE(period) - 1 && period[i] < dev->rep[REP_PERIOD])
+ i++;
+ dev->rep[REP_PERIOD] = period[i];
+
+ while (j < ARRAY_SIZE(delay) - 1 && delay[j] < dev->rep[REP_DELAY])
+ j++;
+ dev->rep[REP_DELAY] = delay[j];
+
+ param = i | (j << 5);
+ return ps2_command(&atkbd->ps2dev, &param, ATKBD_CMD_SETREP);
+}
+
+static int atkbd_set_leds(struct atkbd *atkbd)
+{
+ struct input_dev *dev = atkbd->dev;
+ unsigned char param[2];
+
+ param[0] = (test_bit(LED_SCROLLL, dev->led) ? 1 : 0)
+ | (test_bit(LED_NUML, dev->led) ? 2 : 0)
+ | (test_bit(LED_CAPSL, dev->led) ? 4 : 0);
+ if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+
+ if (atkbd->extra) {
+ param[0] = 0;
+ param[1] = (test_bit(LED_COMPOSE, dev->led) ? 0x01 : 0)
+ | (test_bit(LED_SLEEP, dev->led) ? 0x02 : 0)
+ | (test_bit(LED_SUSPEND, dev->led) ? 0x04 : 0)
+ | (test_bit(LED_MISC, dev->led) ? 0x10 : 0)
+ | (test_bit(LED_MUTE, dev->led) ? 0x20 : 0);
+ if (ps2_command(&atkbd->ps2dev, param, ATKBD_CMD_EX_SETLEDS))
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_event_work() is used to complete processing of events that
+ * can not be processed by input_event() which is often called from
+ * interrupt context.
+ */
+
+static void atkbd_event_work(struct work_struct *work)
+{
+ struct atkbd *atkbd = container_of(work, struct atkbd, event_work.work);
+
+ mutex_lock(&atkbd->event_mutex);
+
+ if (test_and_clear_bit(ATKBD_LED_EVENT_BIT, &atkbd->event_mask))
+ atkbd_set_leds(atkbd);
+
+ if (test_and_clear_bit(ATKBD_REP_EVENT_BIT, &atkbd->event_mask))
+ atkbd_set_repeat_rate(atkbd);
+
+ mutex_unlock(&atkbd->event_mutex);
+}
+
+/*
+ * Schedule switch for execution. We need to throttle requests,
+ * otherwise keyboard may become unresponsive.
+ */
+static void atkbd_schedule_event_work(struct atkbd *atkbd, int event_bit)
+{
+ unsigned long delay = msecs_to_jiffies(50);
+
+ if (time_after(jiffies, atkbd->event_jiffies + delay))
+ delay = 0;
+
+ atkbd->event_jiffies = jiffies;
+ set_bit(event_bit, &atkbd->event_mask);
+ wmb();
+ schedule_delayed_work(&atkbd->event_work, delay);
+}
+
+/*
+ * Event callback from the input module. Events that change the state of
+ * the hardware are processed here. If action can not be performed in
+ * interrupt context it is offloaded to atkbd_event_work.
+ */
+
+static int atkbd_event(struct input_dev *dev,
+ unsigned int type, unsigned int code, int value)
+{
+ struct atkbd *atkbd = input_get_drvdata(dev);
+
+ if (!atkbd->write)
+ return -1;
+
+ switch (type) {
+
+ case EV_LED:
+ atkbd_schedule_event_work(atkbd, ATKBD_LED_EVENT_BIT);
+ return 0;
+
+ case EV_REP:
+ if (!atkbd->softrepeat)
+ atkbd_schedule_event_work(atkbd, ATKBD_REP_EVENT_BIT);
+ return 0;
+ }
+
+ return -1;
+}
+
+/*
+ * atkbd_enable() signals that interrupt handler is allowed to
+ * generate input events.
+ */
+
+static inline void atkbd_enable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 1;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_disable() tells input handler that all incoming data except
+ * for ACKs and command response should be dropped.
+ */
+
+static inline void atkbd_disable(struct atkbd *atkbd)
+{
+ serio_pause_rx(atkbd->ps2dev.serio);
+ atkbd->enabled = 0;
+ serio_continue_rx(atkbd->ps2dev.serio);
+}
+
+/*
+ * atkbd_probe() probes for an AT keyboard on a serio port.
+ */
+
+static int atkbd_probe(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+/*
+ * Some systems, where the bit-twiddling when testing the io-lines of the
+ * controller may confuse the keyboard need a full reset of the keyboard. On
+ * these systems the BIOS also usually doesn't do it for us.
+ */
+
+ if (atkbd_reset)
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_RESET_BAT))
+ printk(KERN_WARNING "atkbd.c: keyboard reset failed on %s\n", ps2dev->serio->phys);
+
+/*
+ * Then we check the keyboard ID. We should get 0xab83 under normal conditions.
+ * Some keyboards report different values, but the first byte is always 0xab or
+ * 0xac. Some old AT keyboards don't report anything. If a mouse is connected, this
+ * should make sure we don't try to set the LEDs on it.
+ */
+
+ param[0] = param[1] = 0xa5; /* initialize with invalid values */
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GETID)) {
+
+/*
+ * If the get ID command failed, we check if we can at least set the LEDs on
+ * the keyboard. This should work on every keyboard out there. It also turns
+ * the LEDs off, which we want anyway.
+ */
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+ atkbd->id = 0xabba;
+ return 0;
+ }
+
+ if (!ps2_is_keyboard_id(param[0]))
+ return -1;
+
+ atkbd->id = (param[0] << 8) | param[1];
+
+ if (atkbd->id == 0xaca1 && atkbd->translated) {
+ printk(KERN_ERR "atkbd.c: NCD terminal keyboards are only supported on non-translating\n");
+ printk(KERN_ERR "atkbd.c: controllers. Use i8042.direct=1 to disable translation.\n");
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_select_set checks if a keyboard has a working Set 3 support, and
+ * sets it into that. Unfortunately there are keyboards that can be switched
+ * to Set 3, but don't work well in that (BTC Multimedia ...)
+ */
+
+static int atkbd_select_set(struct atkbd *atkbd, int target_set, int allow_extra)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[2];
+
+ atkbd->extra = 0;
+/*
+ * For known special keyboards we can go ahead and set the correct set.
+ * We check for NCD PS/2 Sun, NorthGate OmniKey 101 and
+ * IBM RapidAccess / IBM EzButton / Chicony KBP-8993 keyboards.
+ */
+
+ if (atkbd->translated)
+ return 2;
+
+ if (atkbd->id == 0xaca1) {
+ param[0] = 3;
+ ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET);
+ return 3;
+ }
+
+ if (allow_extra) {
+ param[0] = 0x71;
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_EX_ENABLE)) {
+ atkbd->extra = 1;
+ return 2;
+ }
+ }
+
+ if (target_set != 3)
+ return 2;
+
+ if (!ps2_command(ps2dev, param, ATKBD_CMD_OK_GETID)) {
+ atkbd->id = param[0] << 8 | param[1];
+ return 2;
+ }
+
+ param[0] = 3;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_GSCANSET))
+ return 2;
+
+ if (param[0] != 3) {
+ param[0] = 2;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SSCANSET))
+ return 2;
+ }
+
+ ps2_command(ps2dev, param, ATKBD_CMD_SETALL_MBR);
+
+ return 3;
+}
+
+static int atkbd_activate(struct atkbd *atkbd)
+{
+ struct ps2dev *ps2dev = &atkbd->ps2dev;
+ unsigned char param[1];
+
+/*
+ * Set the LEDs to a defined state.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETLEDS))
+ return -1;
+
+/*
+ * Set autorepeat to fastest possible.
+ */
+
+ param[0] = 0;
+ if (ps2_command(ps2dev, param, ATKBD_CMD_SETREP))
+ return -1;
+
+/*
+ * Enable the keyboard to receive keystrokes.
+ */
+
+ if (ps2_command(ps2dev, NULL, ATKBD_CMD_ENABLE)) {
+ printk(KERN_ERR "atkbd.c: Failed to enable keyboard on %s\n",
+ ps2dev->serio->phys);
+ return -1;
+ }
+
+ return 0;
+}
+
+/*
+ * atkbd_cleanup() restores the keyboard state so that BIOS is happy after a
+ * reboot.
+ */
+
+static void atkbd_cleanup(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+
+ atkbd_disable(atkbd);
+ ps2_command(&atkbd->ps2dev, NULL, ATKBD_CMD_RESET_BAT);
+}
+
+
+/*
+ * atkbd_disconnect() closes and frees.
+ */
+
+static void atkbd_disconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+
+ atkbd_disable(atkbd);
+
+ /* make sure we don't have a command in flight */
+ cancel_delayed_work_sync(&atkbd->event_work);
+
+ sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
+ input_unregister_device(atkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(atkbd);
+}
+
+/*
+ * Most special keys (Fn+F?) on Dell laptops do not generate release
+ * events so we have to do it ourselves.
+ */
+static void atkbd_dell_laptop_keymap_fixup(struct atkbd *atkbd)
+{
+ const unsigned int forced_release_keys[] = {
+ 0x85, 0x86, 0x87, 0x88, 0x89, 0x8a, 0x8b, 0x8f, 0x93,
+ };
+ int i;
+
+ if (atkbd->set == 2)
+ for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
+ __set_bit(forced_release_keys[i],
+ atkbd->force_release_mask);
+}
+
+/*
+ * Perform fixup for HP system that doesn't generate release
+ * for its video switch
+ */
+static void atkbd_hp_keymap_fixup(struct atkbd *atkbd)
+{
+ const unsigned int forced_release_keys[] = {
+ 0x94,
+ };
+ int i;
+
+ if (atkbd->set == 2)
+ for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
+ __set_bit(forced_release_keys[i],
+ atkbd->force_release_mask);
+}
+
+/*
+ * Inventec system with broken key release on volume keys
+ */
+static void atkbd_inventec_keymap_fixup(struct atkbd *atkbd)
+{
+ const unsigned int forced_release_keys[] = {
+ 0xae, 0xb0,
+ };
+ int i;
+
+ if (atkbd->set == 2)
+ for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
+ __set_bit(forced_release_keys[i],
+ atkbd->force_release_mask);
+}
+
+/*
+ * Samsung NC10 with Fn+F? key release not working
+ */
+static void atkbd_samsung_keymap_fixup(struct atkbd *atkbd)
+{
+ const unsigned int forced_release_keys[] = {
+ 0x82, 0x83, 0x84, 0x86, 0x88, 0x89, 0xb3, 0xf7, 0xf9,
+ };
+ int i;
+
+ if (atkbd->set == 2)
+ for (i = 0; i < ARRAY_SIZE(forced_release_keys); i++)
+ __set_bit(forced_release_keys[i],
+ atkbd->force_release_mask);
+}
+
+/*
+ * atkbd_set_keycode_table() initializes keyboard's keycode table
+ * according to the selected scancode set
+ */
+
+static void atkbd_set_keycode_table(struct atkbd *atkbd)
+{
+ unsigned int scancode;
+ int i, j;
+
+ memset(atkbd->keycode, 0, sizeof(atkbd->keycode));
+ bitmap_zero(atkbd->force_release_mask, 512);
+
+ if (atkbd->translated) {
+ for (i = 0; i < 128; i++) {
+ scancode = atkbd_unxlate_table[i];
+ atkbd->keycode[i] = atkbd_set2_keycode[scancode];
+ atkbd->keycode[i | 0x80] = atkbd_set2_keycode[scancode | 0x80];
+ if (atkbd->scroll)
+ for (j = 0; j < ARRAY_SIZE(atkbd_scroll_keys); j++)
+ if ((scancode | 0x80) == atkbd_scroll_keys[j].set2)
+ atkbd->keycode[i | 0x80] = atkbd_scroll_keys[j].keycode;
+ }
+ } else if (atkbd->set == 3) {
+ memcpy(atkbd->keycode, atkbd_set3_keycode, sizeof(atkbd->keycode));
+ } else {
+ memcpy(atkbd->keycode, atkbd_set2_keycode, sizeof(atkbd->keycode));
+
+ if (atkbd->scroll)
+ for (i = 0; i < ARRAY_SIZE(atkbd_scroll_keys); i++) {
+ scancode = atkbd_scroll_keys[i].set2;
+ atkbd->keycode[scancode] = atkbd_scroll_keys[i].keycode;
+ }
+ }
+
+/*
+ * HANGEUL and HANJA keys do not send release events so we need to
+ * generate such events ourselves
+ */
+ scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANGEUL);
+ atkbd->keycode[scancode] = KEY_HANGEUL;
+ __set_bit(scancode, atkbd->force_release_mask);
+
+ scancode = atkbd_compat_scancode(atkbd, ATKBD_RET_HANJA);
+ atkbd->keycode[scancode] = KEY_HANJA;
+ __set_bit(scancode, atkbd->force_release_mask);
+
+/*
+ * Perform additional fixups
+ */
+ if (atkbd_platform_fixup)
+ atkbd_platform_fixup(atkbd);
+}
+
+/*
+ * atkbd_set_device_attrs() sets up keyboard's input device structure
+ */
+
+static void atkbd_set_device_attrs(struct atkbd *atkbd)
+{
+ struct input_dev *input_dev = atkbd->dev;
+ int i;
+
+ if (atkbd->extra)
+ snprintf(atkbd->name, sizeof(atkbd->name),
+ "AT Set 2 Extra keyboard");
+ else
+ snprintf(atkbd->name, sizeof(atkbd->name),
+ "AT %s Set %d keyboard",
+ atkbd->translated ? "Translated" : "Raw", atkbd->set);
+
+ snprintf(atkbd->phys, sizeof(atkbd->phys),
+ "%s/input0", atkbd->ps2dev.serio->phys);
+
+ input_dev->name = atkbd->name;
+ input_dev->phys = atkbd->phys;
+ input_dev->id.bustype = BUS_I8042;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = atkbd->translated ? 1 : atkbd->set;
+ input_dev->id.version = atkbd->id;
+ input_dev->event = atkbd_event;
+ input_dev->dev.parent = &atkbd->ps2dev.serio->dev;
+
+ input_set_drvdata(input_dev, atkbd);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+ BIT_MASK(EV_MSC);
+
+ if (atkbd->write) {
+ input_dev->evbit[0] |= BIT_MASK(EV_LED);
+ input_dev->ledbit[0] = BIT_MASK(LED_NUML) |
+ BIT_MASK(LED_CAPSL) | BIT_MASK(LED_SCROLLL);
+ }
+
+ if (atkbd->extra)
+ input_dev->ledbit[0] |= BIT_MASK(LED_COMPOSE) |
+ BIT_MASK(LED_SUSPEND) | BIT_MASK(LED_SLEEP) |
+ BIT_MASK(LED_MUTE) | BIT_MASK(LED_MISC);
+
+ if (!atkbd->softrepeat) {
+ input_dev->rep[REP_DELAY] = 250;
+ input_dev->rep[REP_PERIOD] = 33;
+ }
+
+ input_dev->mscbit[0] = atkbd->softraw ? BIT_MASK(MSC_SCAN) :
+ BIT_MASK(MSC_RAW) | BIT_MASK(MSC_SCAN);
+
+ if (atkbd->scroll) {
+ input_dev->evbit[0] |= BIT_MASK(EV_REL);
+ input_dev->relbit[0] = BIT_MASK(REL_WHEEL) |
+ BIT_MASK(REL_HWHEEL);
+ __set_bit(BTN_MIDDLE, input_dev->keybit);
+ }
+
+ input_dev->keycode = atkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(atkbd_set2_keycode);
+
+ for (i = 0; i < 512; i++)
+ if (atkbd->keycode[i] && atkbd->keycode[i] < ATKBD_SPECIAL)
+ __set_bit(atkbd->keycode[i], input_dev->keybit);
+}
+
+/*
+ * atkbd_connect() is called when the serio module finds an interface
+ * that isn't handled yet by an appropriate device driver. We check if
+ * there is an AT keyboard out there and if yes, we register ourselves
+ * to the input module.
+ */
+
+static int atkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct atkbd *atkbd;
+ struct input_dev *dev;
+ int err = -ENOMEM;
+
+ atkbd = kzalloc(sizeof(struct atkbd), GFP_KERNEL);
+ dev = input_allocate_device();
+ if (!atkbd || !dev)
+ goto fail1;
+
+ atkbd->dev = dev;
+ ps2_init(&atkbd->ps2dev, serio);
+ INIT_DELAYED_WORK(&atkbd->event_work, atkbd_event_work);
+ mutex_init(&atkbd->event_mutex);
+
+ switch (serio->id.type) {
+
+ case SERIO_8042_XL:
+ atkbd->translated = 1;
+ case SERIO_8042:
+ if (serio->write)
+ atkbd->write = 1;
+ break;
+ }
+
+ atkbd->softraw = atkbd_softraw;
+ atkbd->softrepeat = atkbd_softrepeat;
+ atkbd->scroll = atkbd_scroll;
+
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+
+ serio_set_drvdata(serio, atkbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ if (atkbd->write) {
+
+ if (atkbd_probe(atkbd)) {
+ err = -ENODEV;
+ goto fail3;
+ }
+
+ atkbd->set = atkbd_select_set(atkbd, atkbd_set, atkbd_extra);
+ atkbd_activate(atkbd);
+
+ } else {
+ atkbd->set = 2;
+ atkbd->id = 0xab00;
+ }
+
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ err = sysfs_create_group(&serio->dev.kobj, &atkbd_attribute_group);
+ if (err)
+ goto fail3;
+
+ atkbd_enable(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err)
+ goto fail4;
+
+ return 0;
+
+ fail4: sysfs_remove_group(&serio->dev.kobj, &atkbd_attribute_group);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(dev);
+ kfree(atkbd);
+ return err;
+}
+
+/*
+ * atkbd_reconnect() tries to restore keyboard into a sane state and is
+ * most likely called on resume.
+ */
+
+static int atkbd_reconnect(struct serio *serio)
+{
+ struct atkbd *atkbd = serio_get_drvdata(serio);
+ struct serio_driver *drv = serio->drv;
+
+ if (!atkbd || !drv) {
+ printk(KERN_DEBUG "atkbd: reconnect request, but serio is disconnected, ignoring...\n");
+ return -1;
+ }
+
+ atkbd_disable(atkbd);
+
+ if (atkbd->write) {
+ if (atkbd_probe(atkbd))
+ return -1;
+ if (atkbd->set != atkbd_select_set(atkbd, atkbd->set, atkbd->extra))
+ return -1;
+
+ atkbd_activate(atkbd);
+
+/*
+ * Restore repeat rate and LEDs (that were reset by atkbd_activate)
+ * to pre-resume state
+ */
+ if (!atkbd->softrepeat)
+ atkbd_set_repeat_rate(atkbd);
+ atkbd_set_leds(atkbd);
+ }
+
+ atkbd_enable(atkbd);
+
+ return 0;
+}
+
+static struct serio_device_id atkbd_serio_ids[] = {
+ {
+ .type = SERIO_8042,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_8042_XL,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_PS2SER,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, atkbd_serio_ids);
+
+static struct serio_driver atkbd_drv = {
+ .driver = {
+ .name = "atkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = atkbd_serio_ids,
+ .interrupt = atkbd_interrupt,
+ .connect = atkbd_connect,
+ .reconnect = atkbd_reconnect,
+ .disconnect = atkbd_disconnect,
+ .cleanup = atkbd_cleanup,
+};
+
+static ssize_t atkbd_attr_show_helper(struct device *dev, char *buf,
+ ssize_t (*handler)(struct atkbd *, char *))
+{
+ struct serio *serio = to_serio_port(dev);
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ retval = handler((struct atkbd *)serio_get_drvdata(serio), buf);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_attr_set_helper(struct device *dev, const char *buf, size_t count,
+ ssize_t (*handler)(struct atkbd *, const char *, size_t))
+{
+ struct serio *serio = to_serio_port(dev);
+ struct atkbd *atkbd;
+ int retval;
+
+ retval = serio_pin_driver(serio);
+ if (retval)
+ return retval;
+
+ if (serio->drv != &atkbd_drv) {
+ retval = -ENODEV;
+ goto out;
+ }
+
+ atkbd = serio_get_drvdata(serio);
+ atkbd_disable(atkbd);
+ retval = handler(atkbd, buf, count);
+ atkbd_enable(atkbd);
+
+out:
+ serio_unpin_driver(serio);
+ return retval;
+}
+
+static ssize_t atkbd_show_extra(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->extra ? 1 : 0);
+}
+
+static ssize_t atkbd_set_extra(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ struct input_dev *old_dev, *new_dev;
+ unsigned long value;
+ int err;
+ unsigned char old_extra, old_set;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (atkbd->extra != value) {
+ /*
+ * Since device's properties will change we need to
+ * unregister old device. But allocate and register
+ * new one first to make sure we have it.
+ */
+ old_dev = atkbd->dev;
+ old_extra = atkbd->extra;
+ old_set = atkbd->set;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ atkbd->dev = new_dev;
+ atkbd->set = atkbd_select_set(atkbd, atkbd->set, value);
+ atkbd_activate(atkbd);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err) {
+ input_free_device(new_dev);
+
+ atkbd->dev = old_dev;
+ atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ return err;
+ }
+ input_unregister_device(old_dev);
+
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_scroll(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->scroll ? 1 : 0);
+}
+
+static ssize_t atkbd_set_scroll(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ struct input_dev *old_dev, *new_dev;
+ unsigned long value;
+ int err;
+ unsigned char old_scroll;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (atkbd->scroll != value) {
+ old_dev = atkbd->dev;
+ old_scroll = atkbd->scroll;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ atkbd->dev = new_dev;
+ atkbd->scroll = value;
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err) {
+ input_free_device(new_dev);
+
+ atkbd->scroll = old_scroll;
+ atkbd->dev = old_dev;
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ return err;
+ }
+ input_unregister_device(old_dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_set(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->set);
+}
+
+static ssize_t atkbd_set_set(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ struct input_dev *old_dev, *new_dev;
+ unsigned long value;
+ int err;
+ unsigned char old_set, old_extra;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ if (strict_strtoul(buf, 10, &value) || (value != 2 && value != 3))
+ return -EINVAL;
+
+ if (atkbd->set != value) {
+ old_dev = atkbd->dev;
+ old_extra = atkbd->extra;
+ old_set = atkbd->set;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ atkbd->dev = new_dev;
+ atkbd->set = atkbd_select_set(atkbd, value, atkbd->extra);
+ atkbd_activate(atkbd);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err) {
+ input_free_device(new_dev);
+
+ atkbd->dev = old_dev;
+ atkbd->set = atkbd_select_set(atkbd, old_set, old_extra);
+ atkbd_set_keycode_table(atkbd);
+ atkbd_set_device_attrs(atkbd);
+
+ return err;
+ }
+ input_unregister_device(old_dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_softrepeat(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softrepeat ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softrepeat(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ struct input_dev *old_dev, *new_dev;
+ unsigned long value;
+ int err;
+ unsigned char old_softrepeat, old_softraw;
+
+ if (!atkbd->write)
+ return -EIO;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softrepeat != value) {
+ old_dev = atkbd->dev;
+ old_softrepeat = atkbd->softrepeat;
+ old_softraw = atkbd->softraw;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ atkbd->dev = new_dev;
+ atkbd->softrepeat = value;
+ if (atkbd->softrepeat)
+ atkbd->softraw = 1;
+ atkbd_set_device_attrs(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err) {
+ input_free_device(new_dev);
+
+ atkbd->dev = old_dev;
+ atkbd->softrepeat = old_softrepeat;
+ atkbd->softraw = old_softraw;
+ atkbd_set_device_attrs(atkbd);
+
+ return err;
+ }
+ input_unregister_device(old_dev);
+ }
+ return count;
+}
+
+
+static ssize_t atkbd_show_softraw(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%d\n", atkbd->softraw ? 1 : 0);
+}
+
+static ssize_t atkbd_set_softraw(struct atkbd *atkbd, const char *buf, size_t count)
+{
+ struct input_dev *old_dev, *new_dev;
+ unsigned long value;
+ int err;
+ unsigned char old_softraw;
+
+ if (strict_strtoul(buf, 10, &value) || value > 1)
+ return -EINVAL;
+
+ if (atkbd->softraw != value) {
+ old_dev = atkbd->dev;
+ old_softraw = atkbd->softraw;
+
+ new_dev = input_allocate_device();
+ if (!new_dev)
+ return -ENOMEM;
+
+ atkbd->dev = new_dev;
+ atkbd->softraw = value;
+ atkbd_set_device_attrs(atkbd);
+
+ err = input_register_device(atkbd->dev);
+ if (err) {
+ input_free_device(new_dev);
+
+ atkbd->dev = old_dev;
+ atkbd->softraw = old_softraw;
+ atkbd_set_device_attrs(atkbd);
+
+ return err;
+ }
+ input_unregister_device(old_dev);
+ }
+ return count;
+}
+
+static ssize_t atkbd_show_err_count(struct atkbd *atkbd, char *buf)
+{
+ return sprintf(buf, "%lu\n", atkbd->err_count);
+}
+
+static int __init atkbd_setup_fixup(const struct dmi_system_id *id)
+{
+ atkbd_platform_fixup = id->driver_data;
+ return 0;
+}
+
+static struct dmi_system_id atkbd_dmi_quirk_table[] __initdata = {
+ {
+ .ident = "Dell Laptop",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Dell Inc."),
+ DMI_MATCH(DMI_CHASSIS_TYPE, "8"), /* Portable */
+ },
+ .callback = atkbd_setup_fixup,
+ .driver_data = atkbd_dell_laptop_keymap_fixup,
+ },
+ {
+ .ident = "HP 2133",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "HP 2133"),
+ },
+ .callback = atkbd_setup_fixup,
+ .driver_data = atkbd_hp_keymap_fixup,
+ },
+ {
+ .ident = "Inventec Symphony",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "INVENTEC"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "SYMPHONY 6.0/7.0"),
+ },
+ .callback = atkbd_setup_fixup,
+ .driver_data = atkbd_inventec_keymap_fixup,
+ },
+ {
+ .ident = "Samsung NC10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "SAMSUNG ELECTRONICS CO., LTD."),
+ DMI_MATCH(DMI_PRODUCT_NAME, "NC10"),
+ },
+ .callback = atkbd_setup_fixup,
+ .driver_data = atkbd_samsung_keymap_fixup,
+ },
+ { }
+};
+
+static int __init atkbd_init(void)
+{
+ dmi_check_system(atkbd_dmi_quirk_table);
+
+ return serio_register_driver(&atkbd_drv);
+}
+
+static void __exit atkbd_exit(void)
+{
+ serio_unregister_driver(&atkbd_drv);
+}
+
+module_init(atkbd_init);
+module_exit(atkbd_exit);
diff --git a/drivers/input/keyboard/bf54x-keys.c b/drivers/input/keyboard/bf54x-keys.c
new file mode 100644
index 0000000..1928401
--- /dev/null
+++ b/drivers/input/keyboard/bf54x-keys.c
@@ -0,0 +1,421 @@
+/*
+ * File: drivers/input/keyboard/bf54x-keys.c
+ * Based on:
+ * Author: Michael Hennerich <hennerich@blackfin.uclinux.org>
+ *
+ * Created:
+ * Description: keypad driver for Analog Devices Blackfin BF54x Processors
+ *
+ *
+ * Modified:
+ * Copyright 2007-2008 Analog Devices Inc.
+ *
+ * Bugs: Enter bugs at http://blackfin.uclinux.org/
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+
+#include <asm/portmux.h>
+#include <mach/bf54x_keys.h>
+
+#define DRV_NAME "bf54x-keys"
+#define TIME_SCALE 100 /* 100 ns */
+#define MAX_MULT (0xFF * TIME_SCALE)
+#define MAX_RC 8 /* Max Row/Col */
+
+static const u16 per_rows[] = {
+ P_KEY_ROW7,
+ P_KEY_ROW6,
+ P_KEY_ROW5,
+ P_KEY_ROW4,
+ P_KEY_ROW3,
+ P_KEY_ROW2,
+ P_KEY_ROW1,
+ P_KEY_ROW0,
+ 0
+};
+
+static const u16 per_cols[] = {
+ P_KEY_COL7,
+ P_KEY_COL6,
+ P_KEY_COL5,
+ P_KEY_COL4,
+ P_KEY_COL3,
+ P_KEY_COL2,
+ P_KEY_COL1,
+ P_KEY_COL0,
+ 0
+};
+
+struct bf54x_kpad {
+ struct input_dev *input;
+ int irq;
+ unsigned short lastkey;
+ unsigned short *keycode;
+ struct timer_list timer;
+ unsigned int keyup_test_jiffies;
+ unsigned short kpad_msel;
+ unsigned short kpad_prescale;
+ unsigned short kpad_ctl;
+};
+
+static inline int bfin_kpad_find_key(struct bf54x_kpad *bf54x_kpad,
+ struct input_dev *input, u16 keyident)
+{
+ u16 i;
+
+ for (i = 0; i < input->keycodemax; i++)
+ if (bf54x_kpad->keycode[i + input->keycodemax] == keyident)
+ return bf54x_kpad->keycode[i];
+ return -1;
+}
+
+static inline void bfin_keycodecpy(unsigned short *keycode,
+ const unsigned int *pdata_kc,
+ unsigned short keymapsize)
+{
+ unsigned int i;
+
+ for (i = 0; i < keymapsize; i++) {
+ keycode[i] = pdata_kc[i] & 0xffff;
+ keycode[i + keymapsize] = pdata_kc[i] >> 16;
+ }
+}
+
+static inline u16 bfin_kpad_get_prescale(u32 timescale)
+{
+ u32 sclk = get_sclk();
+
+ return ((((sclk / 1000) * timescale) / 1024) - 1);
+}
+
+static inline u16 bfin_kpad_get_keypressed(struct bf54x_kpad *bf54x_kpad)
+{
+ return (bfin_read_KPAD_STAT() & KPAD_PRESSED);
+}
+
+static inline void bfin_kpad_clear_irq(void)
+{
+ bfin_write_KPAD_STAT(0xFFFF);
+ bfin_write_KPAD_ROWCOL(0xFFFF);
+}
+
+static void bfin_kpad_timer(unsigned long data)
+{
+ struct platform_device *pdev = (struct platform_device *) data;
+ struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+ if (bfin_kpad_get_keypressed(bf54x_kpad)) {
+ /* Try again later */
+ mod_timer(&bf54x_kpad->timer,
+ jiffies + bf54x_kpad->keyup_test_jiffies);
+ return;
+ }
+
+ input_report_key(bf54x_kpad->input, bf54x_kpad->lastkey, 0);
+ input_sync(bf54x_kpad->input);
+
+ /* Clear IRQ Status */
+
+ bfin_kpad_clear_irq();
+ enable_irq(bf54x_kpad->irq);
+}
+
+static irqreturn_t bfin_kpad_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+ struct input_dev *input = bf54x_kpad->input;
+ int key;
+ u16 rowcol = bfin_read_KPAD_ROWCOL();
+
+ key = bfin_kpad_find_key(bf54x_kpad, input, rowcol);
+
+ input_report_key(input, key, 1);
+ input_sync(input);
+
+ if (bfin_kpad_get_keypressed(bf54x_kpad)) {
+ disable_irq(bf54x_kpad->irq);
+ bf54x_kpad->lastkey = key;
+ mod_timer(&bf54x_kpad->timer,
+ jiffies + bf54x_kpad->keyup_test_jiffies);
+ } else {
+ input_report_key(input, key, 0);
+ input_sync(input);
+
+ bfin_kpad_clear_irq();
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit bfin_kpad_probe(struct platform_device *pdev)
+{
+ struct bf54x_kpad *bf54x_kpad;
+ struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
+ struct input_dev *input;
+ int i, error;
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ printk(KERN_ERR DRV_NAME
+ ": No rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ if (!pdata->keymapsize ||
+ pdata->keymapsize > (pdata->rows * pdata->cols)) {
+ printk(KERN_ERR DRV_NAME ": Invalid keymapsize\n");
+ return -EINVAL;
+ }
+
+ bf54x_kpad = kzalloc(sizeof(struct bf54x_kpad), GFP_KERNEL);
+ if (!bf54x_kpad)
+ return -ENOMEM;
+
+ platform_set_drvdata(pdev, bf54x_kpad);
+
+ /* Allocate memory for keymap followed by private LUT */
+ bf54x_kpad->keycode = kmalloc(pdata->keymapsize *
+ sizeof(unsigned short) * 2, GFP_KERNEL);
+ if (!bf54x_kpad->keycode) {
+ error = -ENOMEM;
+ goto out;
+ }
+
+ if (!pdata->debounce_time || !pdata->debounce_time > MAX_MULT ||
+ !pdata->coldrive_time || !pdata->coldrive_time > MAX_MULT) {
+ printk(KERN_ERR DRV_NAME
+ ": Invalid Debounce/Columdrive Time from pdata\n");
+ bfin_write_KPAD_MSEL(0xFF0); /* Default MSEL */
+ } else {
+ bfin_write_KPAD_MSEL(
+ ((pdata->debounce_time / TIME_SCALE)
+ & DBON_SCALE) |
+ (((pdata->coldrive_time / TIME_SCALE) << 8)
+ & COLDRV_SCALE));
+
+ }
+
+ if (!pdata->keyup_test_interval)
+ bf54x_kpad->keyup_test_jiffies = msecs_to_jiffies(50);
+ else
+ bf54x_kpad->keyup_test_jiffies =
+ msecs_to_jiffies(pdata->keyup_test_interval);
+
+ if (peripheral_request_list((u16 *)&per_rows[MAX_RC - pdata->rows],
+ DRV_NAME)) {
+ printk(KERN_ERR DRV_NAME
+ ": Requesting Peripherals failed\n");
+ error = -EFAULT;
+ goto out0;
+ }
+
+ if (peripheral_request_list((u16 *)&per_cols[MAX_RC - pdata->cols],
+ DRV_NAME)) {
+ printk(KERN_ERR DRV_NAME
+ ": Requesting Peripherals failed\n");
+ error = -EFAULT;
+ goto out1;
+ }
+
+ bf54x_kpad->irq = platform_get_irq(pdev, 0);
+ if (bf54x_kpad->irq < 0) {
+ error = -ENODEV;
+ goto out2;
+ }
+
+ error = request_irq(bf54x_kpad->irq, bfin_kpad_isr,
+ IRQF_SAMPLE_RANDOM, DRV_NAME, pdev);
+ if (error) {
+ printk(KERN_ERR DRV_NAME
+ ": unable to claim irq %d; error %d\n",
+ bf54x_kpad->irq, error);
+ goto out2;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ error = -ENOMEM;
+ goto out3;
+ }
+
+ bf54x_kpad->input = input;
+
+ input->name = pdev->name;
+ input->phys = "bf54x-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input_set_drvdata(input, bf54x_kpad);
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ input->keycodesize = sizeof(unsigned short);
+ input->keycodemax = pdata->keymapsize;
+ input->keycode = bf54x_kpad->keycode;
+
+ bfin_keycodecpy(bf54x_kpad->keycode, pdata->keymap, pdata->keymapsize);
+
+ /* setup input device */
+ __set_bit(EV_KEY, input->evbit);
+
+ if (pdata->repeat)
+ __set_bit(EV_REP, input->evbit);
+
+ for (i = 0; i < input->keycodemax; i++)
+ __set_bit(bf54x_kpad->keycode[i] & KEY_MAX, input->keybit);
+ __clear_bit(KEY_RESERVED, input->keybit);
+
+ error = input_register_device(input);
+ if (error) {
+ printk(KERN_ERR DRV_NAME
+ ": Unable to register input device (%d)\n", error);
+ goto out4;
+ }
+
+ /* Init Keypad Key Up/Release test timer */
+
+ setup_timer(&bf54x_kpad->timer, bfin_kpad_timer, (unsigned long) pdev);
+
+ bfin_write_KPAD_PRESCALE(bfin_kpad_get_prescale(TIME_SCALE));
+
+ bfin_write_KPAD_CTL((((pdata->cols - 1) << 13) & KPAD_COLEN) |
+ (((pdata->rows - 1) << 10) & KPAD_ROWEN) |
+ (2 & KPAD_IRQMODE));
+
+ bfin_write_KPAD_CTL(bfin_read_KPAD_CTL() | KPAD_EN);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ printk(KERN_ERR DRV_NAME
+ ": Blackfin BF54x Keypad registered IRQ %d\n", bf54x_kpad->irq);
+
+ return 0;
+
+out4:
+ input_free_device(input);
+out3:
+ free_irq(bf54x_kpad->irq, pdev);
+out2:
+ peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
+out1:
+ peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
+out0:
+ kfree(bf54x_kpad->keycode);
+out:
+ kfree(bf54x_kpad);
+ platform_set_drvdata(pdev, NULL);
+
+ return error;
+}
+
+static int __devexit bfin_kpad_remove(struct platform_device *pdev)
+{
+ struct bfin_kpad_platform_data *pdata = pdev->dev.platform_data;
+ struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+ del_timer_sync(&bf54x_kpad->timer);
+ free_irq(bf54x_kpad->irq, pdev);
+
+ input_unregister_device(bf54x_kpad->input);
+
+ peripheral_free_list((u16 *)&per_rows[MAX_RC - pdata->rows]);
+ peripheral_free_list((u16 *)&per_cols[MAX_RC - pdata->cols]);
+
+ kfree(bf54x_kpad->keycode);
+ kfree(bf54x_kpad);
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int bfin_kpad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+ bf54x_kpad->kpad_msel = bfin_read_KPAD_MSEL();
+ bf54x_kpad->kpad_prescale = bfin_read_KPAD_PRESCALE();
+ bf54x_kpad->kpad_ctl = bfin_read_KPAD_CTL();
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(bf54x_kpad->irq);
+
+ return 0;
+}
+
+static int bfin_kpad_resume(struct platform_device *pdev)
+{
+ struct bf54x_kpad *bf54x_kpad = platform_get_drvdata(pdev);
+
+ bfin_write_KPAD_MSEL(bf54x_kpad->kpad_msel);
+ bfin_write_KPAD_PRESCALE(bf54x_kpad->kpad_prescale);
+ bfin_write_KPAD_CTL(bf54x_kpad->kpad_ctl);
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(bf54x_kpad->irq);
+
+ return 0;
+}
+#else
+# define bfin_kpad_suspend NULL
+# define bfin_kpad_resume NULL
+#endif
+
+struct platform_driver bfin_kpad_device_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = bfin_kpad_probe,
+ .remove = __devexit_p(bfin_kpad_remove),
+ .suspend = bfin_kpad_suspend,
+ .resume = bfin_kpad_resume,
+};
+
+static int __init bfin_kpad_init(void)
+{
+ return platform_driver_register(&bfin_kpad_device_driver);
+}
+
+static void __exit bfin_kpad_exit(void)
+{
+ platform_driver_unregister(&bfin_kpad_device_driver);
+}
+
+module_init(bfin_kpad_init);
+module_exit(bfin_kpad_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Michael Hennerich <hennerich@blackfin.uclinux.org>");
+MODULE_DESCRIPTION("Keypad driver for BF54x Processors");
+MODULE_ALIAS("platform:bf54x-keys");
diff --git a/drivers/input/keyboard/corgikbd.c b/drivers/input/keyboard/corgikbd.c
new file mode 100644
index 0000000..c8ed065
--- /dev/null
+++ b/drivers/input/keyboard/corgikbd.c
@@ -0,0 +1,416 @@
+/*
+ * Keyboard driver for Sharp Corgi models (SL-C7xx)
+ *
+ * Copyright (c) 2004-2005 Richard Purdie
+ *
+ * Based on xtkbd.c/locomkbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <mach/corgi.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-gpio.h>
+#include <asm/hardware/scoop.h>
+
+#define KB_ROWS 8
+#define KB_COLS 12
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r,c) ( ((r)<<4) + (c) + 1 )
+/* zero code, 124 scancodes */
+#define NR_SCANCODES ( SCANCODE(KB_ROWS-1,KB_COLS-1) +1 +1 )
+
+#define SCAN_INTERVAL (50) /* ms */
+#define HINGE_SCAN_INTERVAL (250) /* ms */
+
+#define CORGI_KEY_CALENDER KEY_F1
+#define CORGI_KEY_ADDRESS KEY_F2
+#define CORGI_KEY_FN KEY_F3
+#define CORGI_KEY_CANCEL KEY_F4
+#define CORGI_KEY_OFF KEY_SUSPEND
+#define CORGI_KEY_EXOK KEY_F5
+#define CORGI_KEY_EXCANCEL KEY_F6
+#define CORGI_KEY_EXJOGDOWN KEY_F7
+#define CORGI_KEY_EXJOGUP KEY_F8
+#define CORGI_KEY_JAP1 KEY_LEFTCTRL
+#define CORGI_KEY_JAP2 KEY_LEFTALT
+#define CORGI_KEY_MAIL KEY_F10
+#define CORGI_KEY_OK KEY_F11
+#define CORGI_KEY_MENU KEY_F12
+
+static unsigned char corgikbd_keycode[NR_SCANCODES] = {
+ 0, /* 0 */
+ 0, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, 0, 0, 0, 0, 0, 0, 0, /* 1-16 */
+ 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, 0, 0, 0, 0, 0, 0, 0, /* 17-32 */
+ KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
+ CORGI_KEY_CALENDER, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
+ CORGI_KEY_ADDRESS, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, 0, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, /* 65-80 */
+ CORGI_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, 0, CORGI_KEY_FN, 0, 0, 0, 0, /* 81-96 */
+ KEY_SYSRQ, CORGI_KEY_JAP1, CORGI_KEY_JAP2, CORGI_KEY_CANCEL, CORGI_KEY_OK, CORGI_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0, /* 97-112 */
+ CORGI_KEY_OFF, CORGI_KEY_EXOK, CORGI_KEY_EXCANCEL, CORGI_KEY_EXJOGDOWN, CORGI_KEY_EXJOGUP, 0, 0, 0, 0, 0, 0, 0, /* 113-124 */
+};
+
+
+struct corgikbd {
+ unsigned char keycode[ARRAY_SIZE(corgikbd_keycode)];
+ struct input_dev *input;
+
+ spinlock_t lock;
+ struct timer_list timer;
+ struct timer_list htimer;
+
+ unsigned int suspended;
+ unsigned long suspend_jiffies;
+};
+
+#define KB_DISCHARGE_DELAY 10
+#define KB_ACTIVATE_DELAY 10
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using the generic gpio functions to alter
+ * GPDR but it requires a function call per GPIO bit which is
+ * excessive when we need to access 12 bits at once, multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+static inline void corgikbd_discharge_all(void)
+{
+ /* STROBE All HiZ */
+ GPCR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 &= ~CORGI_GPIO_ALL_STROBE_BIT;
+}
+
+static inline void corgikbd_activate_all(void)
+{
+ /* STROBE ALL -> High */
+ GPSR2 = CORGI_GPIO_ALL_STROBE_BIT;
+ GPDR2 |= CORGI_GPIO_ALL_STROBE_BIT;
+
+ udelay(KB_DISCHARGE_DELAY);
+
+ /* Clear any interrupts we may have triggered when altering the GPIO lines */
+ GEDR1 = CORGI_GPIO_HIGH_SENSE_BIT;
+ GEDR2 = CORGI_GPIO_LOW_SENSE_BIT;
+}
+
+static inline void corgikbd_activate_col(int col)
+{
+ /* STROBE col -> High, not col -> HiZ */
+ GPSR2 = CORGI_GPIO_STROBE_BIT(col);
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+static inline void corgikbd_reset_col(int col)
+{
+ /* STROBE col -> Low */
+ GPCR2 = CORGI_GPIO_STROBE_BIT(col);
+ /* STROBE col -> out, not col -> HiZ */
+ GPDR2 = (GPDR2 & ~CORGI_GPIO_ALL_STROBE_BIT) | CORGI_GPIO_STROBE_BIT(col);
+}
+
+#define GET_ROWS_STATUS(c) (((GPLR1 & CORGI_GPIO_HIGH_SENSE_BIT) >> CORGI_GPIO_HIGH_SENSE_RSHIFT) | ((GPLR2 & CORGI_GPIO_LOW_SENSE_BIT) << CORGI_GPIO_LOW_SENSE_LSHIFT))
+
+/*
+ * The corgi keyboard only generates interrupts when a key is pressed.
+ * When a key is pressed, we enable a timer which then scans the
+ * keyboard to detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void corgikbd_scankeyboard(struct corgikbd *corgikbd_data)
+{
+ unsigned int row, col, rowd;
+ unsigned long flags;
+ unsigned int num_pressed;
+
+ if (corgikbd_data->suspended)
+ return;
+
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+ /*
+ * Discharge the output driver capacitatance
+ * in the keyboard matrix. (Yes it is significant..)
+ */
+
+ corgikbd_discharge_all();
+ udelay(KB_DISCHARGE_DELAY);
+
+ corgikbd_activate_col(col);
+ udelay(KB_ACTIVATE_DELAY);
+
+ rowd = GET_ROWS_STATUS(col);
+ for (row = 0; row < KB_ROWS; row++) {
+ unsigned int scancode, pressed;
+
+ scancode = SCANCODE(row, col);
+ pressed = rowd & KB_ROWMASK(row);
+
+ input_report_key(corgikbd_data->input, corgikbd_data->keycode[scancode], pressed);
+
+ if (pressed)
+ num_pressed++;
+
+ if (pressed && (corgikbd_data->keycode[scancode] == CORGI_KEY_OFF)
+ && time_after(jiffies, corgikbd_data->suspend_jiffies + HZ)) {
+ input_event(corgikbd_data->input, EV_PWR, CORGI_KEY_OFF, 1);
+ corgikbd_data->suspend_jiffies=jiffies;
+ }
+ }
+ corgikbd_reset_col(col);
+ }
+
+ corgikbd_activate_all();
+
+ input_sync(corgikbd_data->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&corgikbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+}
+
+/*
+ * corgi keyboard interrupt handler.
+ */
+static irqreturn_t corgikbd_interrupt(int irq, void *dev_id)
+{
+ struct corgikbd *corgikbd_data = dev_id;
+
+ if (!timer_pending(&corgikbd_data->timer)) {
+ /** wait chattering delay **/
+ udelay(20);
+ corgikbd_scankeyboard(corgikbd_data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * corgi timer checking for released keys
+ */
+static void corgikbd_timer_callback(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ corgikbd_scankeyboard(corgikbd_data);
+}
+
+/*
+ * The hinge switches generate no interrupt so they need to be
+ * monitored by a timer.
+ *
+ * We debounce the switches and pass them to the input system.
+ *
+ * gprr == 0x00 - Keyboard with Landscape Screen
+ * 0x08 - No Keyboard with Portrait Screen
+ * 0x0c - Keyboard and Screen Closed
+ */
+
+#define READ_GPIO_BIT(x) (GPLR(x) & GPIO_bit(x))
+#define HINGE_STABLE_COUNT 2
+static int sharpsl_hinge_state;
+static int hinge_count;
+
+static void corgikbd_hinge_timer(unsigned long data)
+{
+ struct corgikbd *corgikbd_data = (struct corgikbd *) data;
+ unsigned long gprr;
+ unsigned long flags;
+
+ gprr = read_scoop_reg(&corgiscoop_device.dev, SCOOP_GPRR) & (CORGI_SCP_SWA | CORGI_SCP_SWB);
+ gprr |= (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0);
+ if (gprr != sharpsl_hinge_state) {
+ hinge_count = 0;
+ sharpsl_hinge_state = gprr;
+ } else if (hinge_count < HINGE_STABLE_COUNT) {
+ hinge_count++;
+ if (hinge_count >= HINGE_STABLE_COUNT) {
+ spin_lock_irqsave(&corgikbd_data->lock, flags);
+
+ input_report_switch(corgikbd_data->input, SW_LID, ((sharpsl_hinge_state & CORGI_SCP_SWA) != 0));
+ input_report_switch(corgikbd_data->input, SW_TABLET_MODE, ((sharpsl_hinge_state & CORGI_SCP_SWB) != 0));
+ input_report_switch(corgikbd_data->input, SW_HEADPHONE_INSERT, (READ_GPIO_BIT(CORGI_GPIO_AK_INT) != 0));
+ input_sync(corgikbd_data->input);
+
+ spin_unlock_irqrestore(&corgikbd_data->lock, flags);
+ }
+ }
+ mod_timer(&corgikbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+}
+
+#ifdef CONFIG_PM
+static int corgikbd_suspend(struct platform_device *dev, pm_message_t state)
+{
+ int i;
+ struct corgikbd *corgikbd = platform_get_drvdata(dev);
+
+ corgikbd->suspended = 1;
+ /* strobe 0 is the power key so this can't be made an input for
+ powersaving therefore i = 1 */
+ for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_IN);
+
+ return 0;
+}
+
+static int corgikbd_resume(struct platform_device *dev)
+{
+ int i;
+ struct corgikbd *corgikbd = platform_get_drvdata(dev);
+
+ for (i = 1; i < CORGI_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ /* Upon resume, ignore the suspend key for a short while */
+ corgikbd->suspend_jiffies=jiffies;
+ corgikbd->suspended = 0;
+
+ return 0;
+}
+#else
+#define corgikbd_suspend NULL
+#define corgikbd_resume NULL
+#endif
+
+static int __init corgikbd_probe(struct platform_device *pdev)
+{
+ struct corgikbd *corgikbd;
+ struct input_dev *input_dev;
+ int i, err = -ENOMEM;
+
+ corgikbd = kzalloc(sizeof(struct corgikbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!corgikbd || !input_dev)
+ goto fail;
+
+ platform_set_drvdata(pdev, corgikbd);
+
+ corgikbd->input = input_dev;
+ spin_lock_init(&corgikbd->lock);
+
+ /* Init Keyboard rescan timer */
+ init_timer(&corgikbd->timer);
+ corgikbd->timer.function = corgikbd_timer_callback;
+ corgikbd->timer.data = (unsigned long) corgikbd;
+
+ /* Init Hinge Timer */
+ init_timer(&corgikbd->htimer);
+ corgikbd->htimer.function = corgikbd_hinge_timer;
+ corgikbd->htimer.data = (unsigned long) corgikbd;
+
+ corgikbd->suspend_jiffies=jiffies;
+
+ memcpy(corgikbd->keycode, corgikbd_keycode, sizeof(corgikbd->keycode));
+
+ input_dev->name = "Corgi Keyboard";
+ input_dev->phys = "corgikbd/input0";
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+ BIT_MASK(EV_PWR) | BIT_MASK(EV_SW);
+ input_dev->keycode = corgikbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(corgikbd_keycode);
+
+ for (i = 0; i < ARRAY_SIZE(corgikbd_keycode); i++)
+ set_bit(corgikbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+ set_bit(SW_LID, input_dev->swbit);
+ set_bit(SW_TABLET_MODE, input_dev->swbit);
+ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
+
+ err = input_register_device(corgikbd->input);
+ if (err)
+ goto fail;
+
+ mod_timer(&corgikbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+
+ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++) {
+ pxa_gpio_mode(CORGI_GPIO_KEY_SENSE(i) | GPIO_IN);
+ if (request_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ "corgikbd", corgikbd))
+ printk(KERN_WARNING "corgikbd: Can't get IRQ: %d!\n", i);
+ }
+
+ /* Set Strobe lines as outputs - set high */
+ for (i = 0; i < CORGI_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(CORGI_GPIO_KEY_STROBE(i) | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ /* Setup the headphone jack as an input */
+ pxa_gpio_mode(CORGI_GPIO_AK_INT | GPIO_IN);
+
+ return 0;
+
+ fail: input_free_device(input_dev);
+ kfree(corgikbd);
+ return err;
+}
+
+static int corgikbd_remove(struct platform_device *pdev)
+{
+ int i;
+ struct corgikbd *corgikbd = platform_get_drvdata(pdev);
+
+ for (i = 0; i < CORGI_KEY_SENSE_NUM; i++)
+ free_irq(CORGI_IRQ_GPIO_KEY_SENSE(i), corgikbd);
+
+ del_timer_sync(&corgikbd->htimer);
+ del_timer_sync(&corgikbd->timer);
+
+ input_unregister_device(corgikbd->input);
+
+ kfree(corgikbd);
+
+ return 0;
+}
+
+static struct platform_driver corgikbd_driver = {
+ .probe = corgikbd_probe,
+ .remove = corgikbd_remove,
+ .suspend = corgikbd_suspend,
+ .resume = corgikbd_resume,
+ .driver = {
+ .name = "corgi-keyboard",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit corgikbd_init(void)
+{
+ return platform_driver_register(&corgikbd_driver);
+}
+
+static void __exit corgikbd_exit(void)
+{
+ platform_driver_unregister(&corgikbd_driver);
+}
+
+module_init(corgikbd_init);
+module_exit(corgikbd_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Corgi Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:corgi-keyboard");
diff --git a/drivers/input/keyboard/gpio_keys.c b/drivers/input/keyboard/gpio_keys.c
new file mode 100644
index 0000000..05f3f43
--- /dev/null
+++ b/drivers/input/keyboard/gpio_keys.c
@@ -0,0 +1,276 @@
+/*
+ * Driver for keys on GPIO lines capable of generating interrupts.
+ *
+ * Copyright 2005 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/gpio_keys.h>
+
+#include <asm/gpio.h>
+
+struct gpio_button_data {
+ struct gpio_keys_button *button;
+ struct input_dev *input;
+ struct timer_list timer;
+};
+
+struct gpio_keys_drvdata {
+ struct input_dev *input;
+ struct gpio_button_data data[0];
+};
+
+static void gpio_keys_report_event(struct gpio_button_data *bdata)
+{
+ struct gpio_keys_button *button = bdata->button;
+ struct input_dev *input = bdata->input;
+ unsigned int type = button->type ?: EV_KEY;
+ int state = (gpio_get_value(button->gpio) ? 1 : 0) ^ button->active_low;
+
+ input_event(input, type, button->code, !!state);
+ input_sync(input);
+}
+
+static void gpio_check_button(unsigned long _data)
+{
+ struct gpio_button_data *data = (struct gpio_button_data *)_data;
+
+ gpio_keys_report_event(data);
+}
+
+static irqreturn_t gpio_keys_isr(int irq, void *dev_id)
+{
+ struct gpio_button_data *bdata = dev_id;
+ struct gpio_keys_button *button = bdata->button;
+
+ BUG_ON(irq != gpio_to_irq(button->gpio));
+
+ if (button->debounce_interval)
+ mod_timer(&bdata->timer,
+ jiffies + msecs_to_jiffies(button->debounce_interval));
+ else
+ gpio_keys_report_event(bdata);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit gpio_keys_probe(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata;
+ struct input_dev *input;
+ int i, error;
+ int wakeup = 0;
+
+ ddata = kzalloc(sizeof(struct gpio_keys_drvdata) +
+ pdata->nbuttons * sizeof(struct gpio_button_data),
+ GFP_KERNEL);
+ input = input_allocate_device();
+ if (!ddata || !input) {
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ platform_set_drvdata(pdev, ddata);
+
+ input->name = pdev->name;
+ input->phys = "gpio-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ ddata->input = input;
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ struct gpio_button_data *bdata = &ddata->data[i];
+ int irq;
+ unsigned int type = button->type ?: EV_KEY;
+
+ bdata->input = input;
+ bdata->button = button;
+ setup_timer(&bdata->timer,
+ gpio_check_button, (unsigned long)bdata);
+
+ error = gpio_request(button->gpio, button->desc ?: "gpio_keys");
+ if (error < 0) {
+ pr_err("gpio-keys: failed to request GPIO %d,"
+ " error %d\n", button->gpio, error);
+ goto fail2;
+ }
+
+ error = gpio_direction_input(button->gpio);
+ if (error < 0) {
+ pr_err("gpio-keys: failed to configure input"
+ " direction for GPIO %d, error %d\n",
+ button->gpio, error);
+ gpio_free(button->gpio);
+ goto fail2;
+ }
+
+ irq = gpio_to_irq(button->gpio);
+ if (irq < 0) {
+ error = irq;
+ pr_err("gpio-keys: Unable to get irq number"
+ " for GPIO %d, error %d\n",
+ button->gpio, error);
+ gpio_free(button->gpio);
+ goto fail2;
+ }
+
+ error = request_irq(irq, gpio_keys_isr,
+ IRQF_SAMPLE_RANDOM | IRQF_TRIGGER_RISING |
+ IRQF_TRIGGER_FALLING,
+ button->desc ? button->desc : "gpio_keys",
+ bdata);
+ if (error) {
+ pr_err("gpio-keys: Unable to claim irq %d; error %d\n",
+ irq, error);
+ gpio_free(button->gpio);
+ goto fail2;
+ }
+
+ if (button->wakeup)
+ wakeup = 1;
+
+ input_set_capability(input, type, button->code);
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ pr_err("gpio-keys: Unable to register input device, "
+ "error: %d\n", error);
+ goto fail2;
+ }
+
+ device_init_wakeup(&pdev->dev, wakeup);
+
+ return 0;
+
+ fail2:
+ while (--i >= 0) {
+ free_irq(gpio_to_irq(pdata->buttons[i].gpio), &ddata->data[i]);
+ if (pdata->buttons[i].debounce_interval)
+ del_timer_sync(&ddata->data[i].timer);
+ gpio_free(pdata->buttons[i].gpio);
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ fail1:
+ input_free_device(input);
+ kfree(ddata);
+
+ return error;
+}
+
+static int __devexit gpio_keys_remove(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ struct gpio_keys_drvdata *ddata = platform_get_drvdata(pdev);
+ struct input_dev *input = ddata->input;
+ int i;
+
+ device_init_wakeup(&pdev->dev, 0);
+
+ for (i = 0; i < pdata->nbuttons; i++) {
+ int irq = gpio_to_irq(pdata->buttons[i].gpio);
+ free_irq(irq, &ddata->data[i]);
+ if (pdata->buttons[i].debounce_interval)
+ del_timer_sync(&ddata->data[i].timer);
+ gpio_free(pdata->buttons[i].gpio);
+ }
+
+ input_unregister_device(input);
+
+ return 0;
+}
+
+
+#ifdef CONFIG_PM
+static int gpio_keys_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ if (button->wakeup) {
+ int irq = gpio_to_irq(button->gpio);
+ enable_irq_wake(irq);
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int gpio_keys_resume(struct platform_device *pdev)
+{
+ struct gpio_keys_platform_data *pdata = pdev->dev.platform_data;
+ int i;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ for (i = 0; i < pdata->nbuttons; i++) {
+ struct gpio_keys_button *button = &pdata->buttons[i];
+ if (button->wakeup) {
+ int irq = gpio_to_irq(button->gpio);
+ disable_irq_wake(irq);
+ }
+ }
+ }
+
+ return 0;
+}
+#else
+#define gpio_keys_suspend NULL
+#define gpio_keys_resume NULL
+#endif
+
+static struct platform_driver gpio_keys_device_driver = {
+ .probe = gpio_keys_probe,
+ .remove = __devexit_p(gpio_keys_remove),
+ .suspend = gpio_keys_suspend,
+ .resume = gpio_keys_resume,
+ .driver = {
+ .name = "gpio-keys",
+ .owner = THIS_MODULE,
+ }
+};
+
+static int __init gpio_keys_init(void)
+{
+ return platform_driver_register(&gpio_keys_device_driver);
+}
+
+static void __exit gpio_keys_exit(void)
+{
+ platform_driver_unregister(&gpio_keys_device_driver);
+}
+
+module_init(gpio_keys_init);
+module_exit(gpio_keys_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Phil Blundell <pb@handhelds.org>");
+MODULE_DESCRIPTION("Keyboard driver for CPU GPIOs");
+MODULE_ALIAS("platform:gpio-keys");
diff --git a/drivers/input/keyboard/hil_kbd.c b/drivers/input/keyboard/hil_kbd.c
new file mode 100644
index 0000000..71c1971
--- /dev/null
+++ b/drivers/input/keyboard/hil_kbd.c
@@ -0,0 +1,402 @@
+/*
+ * Generic linux-input device driver for keyboard devices
+ *
+ * Copyright (c) 2001 Brian S. Julin
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions, and the following disclaimer,
+ * without modification.
+ * 2. The name of the author may not be used to endorse or promote products
+ * derived from this software without specific prior written permission.
+ *
+ * Alternatively, this software may be distributed under the terms of the
+ * GNU General Public License ("GPL").
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE FOR
+ * ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ *
+ * References:
+ * HP-HIL Technical Reference Manual. Hewlett Packard Product No. 45918A
+ *
+ */
+
+#include <linux/hil.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/semaphore.h>
+#include <linux/slab.h>
+#include <linux/pci_ids.h>
+
+#define PREFIX "HIL KEYB: "
+#define HIL_GENERIC_NAME "HIL keyboard"
+
+MODULE_AUTHOR("Brian S. Julin <bri@calyx.com>");
+MODULE_DESCRIPTION(HIL_GENERIC_NAME " driver");
+MODULE_LICENSE("Dual BSD/GPL");
+
+#define HIL_KBD_MAX_LENGTH 16
+
+#define HIL_KBD_SET1_UPBIT 0x01
+#define HIL_KBD_SET1_SHIFT 1
+static unsigned int hil_kbd_set1[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
+ { HIL_KEYCODES_SET1 };
+
+#define HIL_KBD_SET2_UPBIT 0x01
+#define HIL_KBD_SET2_SHIFT 1
+/* Set2 is user defined */
+
+#define HIL_KBD_SET3_UPBIT 0x80
+#define HIL_KBD_SET3_SHIFT 0
+static unsigned int hil_kbd_set3[HIL_KEYCODES_SET3_TBLSIZE] __read_mostly =
+ { HIL_KEYCODES_SET3 };
+
+static const char hil_language[][16] = { HIL_LOCALE_MAP };
+
+struct hil_kbd {
+ struct input_dev *dev;
+ struct serio *serio;
+
+ /* Input buffer and index for packets from HIL bus. */
+ hil_packet data[HIL_KBD_MAX_LENGTH];
+ int idx4; /* four counts per packet */
+
+ /* Raw device info records from HIL bus, see hil.h for fields. */
+ char idd[HIL_KBD_MAX_LENGTH]; /* DID byte and IDD record */
+ char rsc[HIL_KBD_MAX_LENGTH]; /* RSC record */
+ char exd[HIL_KBD_MAX_LENGTH]; /* EXD record */
+ char rnm[HIL_KBD_MAX_LENGTH + 1]; /* RNM record + NULL term. */
+
+ /* Something to sleep around with. */
+ struct semaphore sem;
+};
+
+/* Process a complete packet after transfer from the HIL */
+static void hil_kbd_process_record(struct hil_kbd *kbd)
+{
+ struct input_dev *dev = kbd->dev;
+ hil_packet *data = kbd->data;
+ hil_packet p;
+ int idx, i, cnt;
+
+ idx = kbd->idx4/4;
+ p = data[idx - 1];
+
+ if ((p & ~HIL_CMDCT_POL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_POL))
+ goto report;
+ if ((p & ~HIL_CMDCT_RPL) ==
+ (HIL_ERR_INT | HIL_PKT_CMD | HIL_CMD_RPL))
+ goto report;
+
+ /* Not a poll response. See if we are loading config records. */
+ switch (p & HIL_PKT_DATA_MASK) {
+ case HIL_CMD_IDD:
+ for (i = 0; i < idx; i++)
+ kbd->idd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->idd[i] = 0;
+ break;
+
+ case HIL_CMD_RSC:
+ for (i = 0; i < idx; i++)
+ kbd->rsc[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->rsc[i] = 0;
+ break;
+
+ case HIL_CMD_EXD:
+ for (i = 0; i < idx; i++)
+ kbd->exd[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH; i++)
+ kbd->exd[i] = 0;
+ break;
+
+ case HIL_CMD_RNM:
+ for (i = 0; i < idx; i++)
+ kbd->rnm[i] = kbd->data[i] & HIL_PKT_DATA_MASK;
+ for (; i < HIL_KBD_MAX_LENGTH + 1; i++)
+ kbd->rnm[i] = '\0';
+ break;
+
+ default:
+ /* These occur when device isn't present */
+ if (p == (HIL_ERR_INT | HIL_PKT_CMD))
+ break;
+ /* Anything else we'd like to know about. */
+ printk(KERN_WARNING PREFIX "Device sent unknown record %x\n", p);
+ break;
+ }
+ goto out;
+
+ report:
+ cnt = 1;
+ switch (kbd->data[0] & HIL_POL_CHARTYPE_MASK) {
+ case HIL_POL_CHARTYPE_NONE:
+ break;
+
+ case HIL_POL_CHARTYPE_ASCII:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++] & 0x7f, 1);
+ break;
+
+ case HIL_POL_CHARTYPE_RSVD1:
+ case HIL_POL_CHARTYPE_RSVD2:
+ case HIL_POL_CHARTYPE_BINARY:
+ while (cnt < idx - 1)
+ input_report_key(dev, kbd->data[cnt++], 1);
+ break;
+
+ case HIL_POL_CHARTYPE_SET1:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET1_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set1[key >> HIL_KBD_SET1_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+
+ case HIL_POL_CHARTYPE_SET2:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET2_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = key >> HIL_KBD_SET2_SHIFT;
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+
+ case HIL_POL_CHARTYPE_SET3:
+ while (cnt < idx - 1) {
+ unsigned int key;
+ int up;
+ key = kbd->data[cnt++];
+ up = key & HIL_KBD_SET3_UPBIT;
+ key &= (~HIL_KBD_SET1_UPBIT & 0xff);
+ key = hil_kbd_set3[key >> HIL_KBD_SET3_SHIFT];
+ if (key != KEY_RESERVED)
+ input_report_key(dev, key, !up);
+ }
+ break;
+ }
+ out:
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static void hil_kbd_process_err(struct hil_kbd *kbd)
+{
+ printk(KERN_WARNING PREFIX "errored HIL packet\n");
+ kbd->idx4 = 0;
+ up(&kbd->sem);
+}
+
+static irqreturn_t hil_kbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct hil_kbd *kbd;
+ hil_packet packet;
+ int idx;
+
+ kbd = serio_get_drvdata(serio);
+ BUG_ON(kbd == NULL);
+
+ if (kbd->idx4 >= (HIL_KBD_MAX_LENGTH * sizeof(hil_packet))) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ idx = kbd->idx4/4;
+ if (!(kbd->idx4 % 4))
+ kbd->data[idx] = 0;
+ packet = kbd->data[idx];
+ packet |= ((hil_packet)data) << ((3 - (kbd->idx4 % 4)) * 8);
+ kbd->data[idx] = packet;
+
+ /* Records of N 4-byte hil_packets must terminate with a command. */
+ if ((++(kbd->idx4)) % 4)
+ return IRQ_HANDLED;
+ if ((packet & 0xffff0000) != HIL_ERR_INT) {
+ hil_kbd_process_err(kbd);
+ return IRQ_HANDLED;
+ }
+ if (packet & HIL_PKT_CMD)
+ hil_kbd_process_record(kbd);
+ return IRQ_HANDLED;
+}
+
+static void hil_kbd_disconnect(struct serio *serio)
+{
+ struct hil_kbd *kbd;
+
+ kbd = serio_get_drvdata(serio);
+ BUG_ON(kbd == NULL);
+
+ serio_close(serio);
+ input_unregister_device(kbd->dev);
+ kfree(kbd);
+}
+
+static int hil_kbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct hil_kbd *kbd;
+ uint8_t did, *idd;
+ int i;
+
+ kbd = kzalloc(sizeof(*kbd), GFP_KERNEL);
+ if (!kbd)
+ return -ENOMEM;
+
+ kbd->dev = input_allocate_device();
+ if (!kbd->dev)
+ goto bail0;
+
+ if (serio_open(serio, drv))
+ goto bail1;
+
+ serio_set_drvdata(serio, kbd);
+ kbd->serio = serio;
+
+ init_MUTEX_LOCKED(&kbd->sem);
+
+ /* Get device info. MLC driver supplies devid/status/etc. */
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_IDD);
+ down(&kbd->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RSC);
+ down(&kbd->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_RNM);
+ down(&kbd->sem);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EXD);
+ down(&kbd->sem);
+
+ up(&kbd->sem);
+
+ did = kbd->idd[0];
+ idd = kbd->idd + 1;
+ switch (did & HIL_IDD_DID_TYPE_MASK) {
+ case HIL_IDD_DID_TYPE_KB_INTEGRAL:
+ case HIL_IDD_DID_TYPE_KB_ITF:
+ case HIL_IDD_DID_TYPE_KB_RSVD:
+ case HIL_IDD_DID_TYPE_CHAR:
+ printk(KERN_INFO PREFIX "HIL keyboard found (did = 0x%02x, lang = %s)\n",
+ did, hil_language[did & HIL_IDD_DID_TYPE_KB_LANG_MASK]);
+ break;
+ default:
+ goto bail2;
+ }
+
+ if (HIL_IDD_NUM_BUTTONS(idd) || HIL_IDD_NUM_AXES_PER_SET(*idd)) {
+ printk(KERN_INFO PREFIX "keyboards only, no combo devices supported.\n");
+ goto bail2;
+ }
+
+ kbd->dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ kbd->dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+ BIT_MASK(LED_SCROLLL);
+ kbd->dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ kbd->dev->keycodesize = sizeof(hil_kbd_set1[0]);
+ kbd->dev->keycode = hil_kbd_set1;
+ kbd->dev->name = strlen(kbd->rnm) ? kbd->rnm : HIL_GENERIC_NAME;
+ kbd->dev->phys = "hpkbd/input0"; /* XXX */
+
+ kbd->dev->id.bustype = BUS_HIL;
+ kbd->dev->id.vendor = PCI_VENDOR_ID_HP;
+ kbd->dev->id.product = 0x0001; /* TODO: get from kbd->rsc */
+ kbd->dev->id.version = 0x0100; /* TODO: get from kbd->rsc */
+ kbd->dev->dev.parent = &serio->dev;
+
+ for (i = 0; i < 128; i++) {
+ set_bit(hil_kbd_set1[i], kbd->dev->keybit);
+ set_bit(hil_kbd_set3[i], kbd->dev->keybit);
+ }
+ clear_bit(0, kbd->dev->keybit);
+
+ input_register_device(kbd->dev);
+ printk(KERN_INFO "input: %s, ID: %d\n",
+ kbd->dev->name, did);
+
+ serio->write(serio, 0);
+ serio->write(serio, 0);
+ serio->write(serio, HIL_PKT_CMD >> 8);
+ serio->write(serio, HIL_CMD_EK1); /* Enable Keyswitch Autorepeat 1 */
+ down(&kbd->sem);
+ up(&kbd->sem);
+
+ return 0;
+ bail2:
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ bail1:
+ input_free_device(kbd->dev);
+ bail0:
+ kfree(kbd);
+ return -EIO;
+}
+
+static struct serio_device_id hil_kbd_ids[] = {
+ {
+ .type = SERIO_HIL_MLC,
+ .proto = SERIO_HIL,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+static struct serio_driver hil_kbd_serio_drv = {
+ .driver = {
+ .name = "hil_kbd",
+ },
+ .description = "HP HIL keyboard driver",
+ .id_table = hil_kbd_ids,
+ .connect = hil_kbd_connect,
+ .disconnect = hil_kbd_disconnect,
+ .interrupt = hil_kbd_interrupt
+};
+
+static int __init hil_kbd_init(void)
+{
+ return serio_register_driver(&hil_kbd_serio_drv);
+}
+
+static void __exit hil_kbd_exit(void)
+{
+ serio_unregister_driver(&hil_kbd_serio_drv);
+}
+
+module_init(hil_kbd_init);
+module_exit(hil_kbd_exit);
diff --git a/drivers/input/keyboard/hilkbd.c b/drivers/input/keyboard/hilkbd.c
new file mode 100644
index 0000000..aacf71f
--- /dev/null
+++ b/drivers/input/keyboard/hilkbd.c
@@ -0,0 +1,377 @@
+/*
+ * linux/drivers/hil/hilkbd.c
+ *
+ * Copyright (C) 1998 Philip Blundell <philb@gnu.org>
+ * Copyright (C) 1999 Matthew Wilcox <willy@bofh.ai>
+ * Copyright (C) 1999-2007 Helge Deller <deller@gmx.de>
+ *
+ * Very basic HP Human Interface Loop (HIL) driver.
+ * This driver handles the keyboard on HP300 (m68k) and on some
+ * HP700 (parisc) series machines.
+ *
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License version 2. See the file COPYING in the main directory of this
+ * archive for more details.
+ */
+
+#include <linux/pci_ids.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/errno.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/hil.h>
+#include <linux/io.h>
+#include <linux/spinlock.h>
+#include <asm/irq.h>
+#ifdef CONFIG_HP300
+#include <asm/hwtest.h>
+#endif
+
+
+MODULE_AUTHOR("Philip Blundell, Matthew Wilcox, Helge Deller");
+MODULE_DESCRIPTION("HIL keyboard driver (basic functionality)");
+MODULE_LICENSE("GPL v2");
+
+
+#if defined(CONFIG_PARISC)
+
+ #include <asm/io.h>
+ #include <asm/hardware.h>
+ #include <asm/parisc-device.h>
+ static unsigned long hil_base; /* HPA for the HIL device */
+ static unsigned int hil_irq;
+ #define HILBASE hil_base /* HPPA (parisc) port address */
+ #define HIL_DATA 0x800
+ #define HIL_CMD 0x801
+ #define HIL_IRQ hil_irq
+ #define hil_readb(p) gsc_readb(p)
+ #define hil_writeb(v,p) gsc_writeb((v),(p))
+
+#elif defined(CONFIG_HP300)
+
+ #define HILBASE 0xf0428000UL /* HP300 (m68k) port address */
+ #define HIL_DATA 0x1
+ #define HIL_CMD 0x3
+ #define HIL_IRQ 2
+ #define hil_readb(p) readb(p)
+ #define hil_writeb(v,p) writeb((v),(p))
+
+#else
+#error "HIL is not supported on this platform"
+#endif
+
+
+
+/* HIL helper functions */
+
+#define hil_busy() (hil_readb(HILBASE + HIL_CMD) & HIL_BUSY)
+#define hil_data_available() (hil_readb(HILBASE + HIL_CMD) & HIL_DATA_RDY)
+#define hil_status() (hil_readb(HILBASE + HIL_CMD))
+#define hil_command(x) do { hil_writeb((x), HILBASE + HIL_CMD); } while (0)
+#define hil_read_data() (hil_readb(HILBASE + HIL_DATA))
+#define hil_write_data(x) do { hil_writeb((x), HILBASE + HIL_DATA); } while (0)
+
+/* HIL constants */
+
+#define HIL_BUSY 0x02
+#define HIL_DATA_RDY 0x01
+
+#define HIL_SETARD 0xA0 /* set auto-repeat delay */
+#define HIL_SETARR 0xA2 /* set auto-repeat rate */
+#define HIL_SETTONE 0xA3 /* set tone generator */
+#define HIL_CNMT 0xB2 /* clear nmi */
+#define HIL_INTON 0x5C /* Turn on interrupts. */
+#define HIL_INTOFF 0x5D /* Turn off interrupts. */
+
+#define HIL_READKBDSADR 0xF9
+#define HIL_WRITEKBDSADR 0xE9
+
+static unsigned int hphilkeyb_keycode[HIL_KEYCODES_SET1_TBLSIZE] __read_mostly =
+ { HIL_KEYCODES_SET1 };
+
+/* HIL structure */
+static struct {
+ struct input_dev *dev;
+
+ unsigned int curdev;
+
+ unsigned char s;
+ unsigned char c;
+ int valid;
+
+ unsigned char data[16];
+ unsigned int ptr;
+ spinlock_t lock;
+
+ void *dev_id; /* native bus device */
+} hil_dev;
+
+
+static void poll_finished(void)
+{
+ int down;
+ int key;
+ unsigned char scode;
+
+ switch (hil_dev.data[0]) {
+ case 0x40:
+ down = (hil_dev.data[1] & 1) == 0;
+ scode = hil_dev.data[1] >> 1;
+ key = hphilkeyb_keycode[scode];
+ input_report_key(hil_dev.dev, key, down);
+ break;
+ }
+ hil_dev.curdev = 0;
+}
+
+
+static inline void handle_status(unsigned char s, unsigned char c)
+{
+ if (c & 0x8) {
+ /* End of block */
+ if (c & 0x10)
+ poll_finished();
+ } else {
+ if (c & 0x10) {
+ if (hil_dev.curdev)
+ poll_finished(); /* just in case */
+ hil_dev.curdev = c & 7;
+ hil_dev.ptr = 0;
+ }
+ }
+}
+
+
+static inline void handle_data(unsigned char s, unsigned char c)
+{
+ if (hil_dev.curdev) {
+ hil_dev.data[hil_dev.ptr++] = c;
+ hil_dev.ptr &= 15;
+ }
+}
+
+
+/* handle HIL interrupts */
+static irqreturn_t hil_interrupt(int irq, void *handle)
+{
+ unsigned char s, c;
+
+ s = hil_status();
+ c = hil_read_data();
+
+ switch (s >> 4) {
+ case 0x5:
+ handle_status(s, c);
+ break;
+ case 0x6:
+ handle_data(s, c);
+ break;
+ case 0x4:
+ hil_dev.s = s;
+ hil_dev.c = c;
+ mb();
+ hil_dev.valid = 1;
+ break;
+ }
+ return IRQ_HANDLED;
+}
+
+
+/* send a command to the HIL */
+static void hil_do(unsigned char cmd, unsigned char *data, unsigned int len)
+{
+ unsigned long flags;
+
+ spin_lock_irqsave(&hil_dev.lock, flags);
+ while (hil_busy())
+ /* wait */;
+ hil_command(cmd);
+ while (len--) {
+ while (hil_busy())
+ /* wait */;
+ hil_write_data(*(data++));
+ }
+ spin_unlock_irqrestore(&hil_dev.lock, flags);
+}
+
+
+/* initialise HIL */
+static int __init
+hil_keyb_init(void)
+{
+ unsigned char c;
+ unsigned int i, kbid;
+ wait_queue_head_t hil_wait;
+ int err;
+
+ if (hil_dev.dev) {
+ return -ENODEV; /* already initialized */
+ }
+
+ spin_lock_init(&hil_dev.lock);
+ hil_dev.dev = input_allocate_device();
+ if (!hil_dev.dev)
+ return -ENOMEM;
+
+#if defined(CONFIG_HP300)
+ if (!MACH_IS_HP300) {
+ err = -ENODEV;
+ goto err1;
+ }
+ if (!hwreg_present((void *)(HILBASE + HIL_DATA))) {
+ printk(KERN_ERR "HIL: hardware register was not found\n");
+ err = -ENODEV;
+ goto err1;
+ }
+ if (!request_region(HILBASE + HIL_DATA, 2, "hil")) {
+ printk(KERN_ERR "HIL: IOPORT region already used\n");
+ err = -EIO;
+ goto err1;
+ }
+#endif
+
+ err = request_irq(HIL_IRQ, hil_interrupt, 0, "hil", hil_dev.dev_id);
+ if (err) {
+ printk(KERN_ERR "HIL: Can't get IRQ\n");
+ goto err2;
+ }
+
+ /* Turn on interrupts */
+ hil_do(HIL_INTON, NULL, 0);
+
+ /* Look for keyboards */
+ hil_dev.valid = 0; /* clear any pending data */
+ hil_do(HIL_READKBDSADR, NULL, 0);
+
+ init_waitqueue_head(&hil_wait);
+ wait_event_interruptible_timeout(hil_wait, hil_dev.valid, 3*HZ);
+ if (!hil_dev.valid) {
+ printk(KERN_WARNING "HIL: timed out, assuming no keyboard present\n");
+ }
+
+ c = hil_dev.c;
+ hil_dev.valid = 0;
+ if (c == 0) {
+ kbid = -1;
+ printk(KERN_WARNING "HIL: no keyboard present\n");
+ } else {
+ kbid = ffz(~c);
+ printk(KERN_INFO "HIL: keyboard found at id %d\n", kbid);
+ }
+
+ /* set it to raw mode */
+ c = 0;
+ hil_do(HIL_WRITEKBDSADR, &c, 1);
+
+ for (i = 0; i < HIL_KEYCODES_SET1_TBLSIZE; i++)
+ if (hphilkeyb_keycode[i] != KEY_RESERVED)
+ set_bit(hphilkeyb_keycode[i], hil_dev.dev->keybit);
+
+ hil_dev.dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ hil_dev.dev->ledbit[0] = BIT_MASK(LED_NUML) | BIT_MASK(LED_CAPSL) |
+ BIT_MASK(LED_SCROLLL);
+ hil_dev.dev->keycodemax = HIL_KEYCODES_SET1_TBLSIZE;
+ hil_dev.dev->keycodesize= sizeof(hphilkeyb_keycode[0]);
+ hil_dev.dev->keycode = hphilkeyb_keycode;
+ hil_dev.dev->name = "HIL keyboard";
+ hil_dev.dev->phys = "hpkbd/input0";
+
+ hil_dev.dev->id.bustype = BUS_HIL;
+ hil_dev.dev->id.vendor = PCI_VENDOR_ID_HP;
+ hil_dev.dev->id.product = 0x0001;
+ hil_dev.dev->id.version = 0x0010;
+
+ err = input_register_device(hil_dev.dev);
+ if (err) {
+ printk(KERN_ERR "HIL: Can't register device\n");
+ goto err3;
+ }
+ printk(KERN_INFO "input: %s, ID %d at 0x%08lx (irq %d) found and attached\n",
+ hil_dev.dev->name, kbid, HILBASE, HIL_IRQ);
+
+ return 0;
+
+err3:
+ hil_do(HIL_INTOFF, NULL, 0);
+ disable_irq(HIL_IRQ);
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+err2:
+#if defined(CONFIG_HP300)
+ release_region(HILBASE + HIL_DATA, 2);
+err1:
+#endif
+ input_free_device(hil_dev.dev);
+ hil_dev.dev = NULL;
+ return err;
+}
+
+
+#if defined(CONFIG_PARISC)
+static int __init
+hil_init_chip(struct parisc_device *dev)
+{
+ if (!dev->irq) {
+ printk(KERN_WARNING "HIL: IRQ not found for HIL bus at 0x%08lx\n", dev->hpa.start);
+ return -ENODEV;
+ }
+
+ hil_base = dev->hpa.start;
+ hil_irq = dev->irq;
+ hil_dev.dev_id = dev;
+
+ printk(KERN_INFO "Found HIL bus at 0x%08lx, IRQ %d\n", hil_base, hil_irq);
+
+ return hil_keyb_init();
+}
+
+static struct parisc_device_id hil_tbl[] = {
+ { HPHW_FIO, HVERSION_REV_ANY_ID, HVERSION_ANY_ID, 0x00073 },
+ { 0, }
+};
+
+MODULE_DEVICE_TABLE(parisc, hil_tbl);
+
+static struct parisc_driver hil_driver = {
+ .name = "hil",
+ .id_table = hil_tbl,
+ .probe = hil_init_chip,
+};
+#endif /* CONFIG_PARISC */
+
+
+static int __init hil_init(void)
+{
+#if defined(CONFIG_PARISC)
+ return register_parisc_driver(&hil_driver);
+#else
+ return hil_keyb_init();
+#endif
+}
+
+
+static void __exit hil_exit(void)
+{
+ if (HIL_IRQ) {
+ disable_irq(HIL_IRQ);
+ free_irq(HIL_IRQ, hil_dev.dev_id);
+ }
+
+ /* Turn off interrupts */
+ hil_do(HIL_INTOFF, NULL, 0);
+
+ input_unregister_device(hil_dev.dev);
+
+ hil_dev.dev = NULL;
+
+#if defined(CONFIG_PARISC)
+ unregister_parisc_driver(&hil_driver);
+#else
+ release_region(HILBASE+HIL_DATA, 2);
+#endif
+}
+
+module_init(hil_init);
+module_exit(hil_exit);
diff --git a/drivers/input/keyboard/hpps2atkbd.h b/drivers/input/keyboard/hpps2atkbd.h
new file mode 100644
index 0000000..dc33f69
--- /dev/null
+++ b/drivers/input/keyboard/hpps2atkbd.h
@@ -0,0 +1,110 @@
+/*
+ * drivers/input/keyboard/hpps2atkbd.h
+ *
+ * Copyright (c) 2004 Helge Deller <deller@gmx.de>
+ * Copyright (c) 2002 Laurent Canet <canetl@esiee.fr>
+ * Copyright (c) 2002 Thibaut Varene <varenet@parisc-linux.org>
+ * Copyright (c) 2000 Xavier Debacker <debackex@esiee.fr>
+ *
+ * HP PS/2 AT-compatible Keyboard, found in PA/RISC Workstations & Laptops
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+
+
+/* Is the keyboard an RDI PrecisionBook? */
+#ifndef CONFIG_KEYBOARD_ATKBD_RDI_KEYCODES
+# define CONFLICT(x,y) x
+#else
+# define CONFLICT(x,y) y
+#endif
+
+/* sadly RDI (Tadpole) decided to ship a different keyboard layout
+ than HP for their PS/2 laptop keyboard which leads to conflicting
+ keycodes between a normal HP PS/2 keyboard and a RDI Precisionbook.
+ HP: RDI: */
+#define C_07 CONFLICT( KEY_F12, KEY_F1 )
+#define C_11 CONFLICT( KEY_LEFTALT, KEY_LEFTCTRL )
+#define C_14 CONFLICT( KEY_LEFTCTRL, KEY_CAPSLOCK )
+#define C_58 CONFLICT( KEY_CAPSLOCK, KEY_RIGHTCTRL )
+#define C_61 CONFLICT( KEY_102ND, KEY_LEFT )
+
+/* Raw SET 2 scancode table */
+
+/* 00 */ KEY_RESERVED, KEY_F9, KEY_RESERVED, KEY_F5, KEY_F3, KEY_F1, KEY_F2, C_07,
+/* 08 */ KEY_ESC, KEY_F10, KEY_F8, KEY_F6, KEY_F4, KEY_TAB, KEY_GRAVE, KEY_F2,
+/* 10 */ KEY_RESERVED, C_11, KEY_LEFTSHIFT, KEY_RESERVED, C_14, KEY_Q, KEY_1, KEY_F3,
+/* 18 */ KEY_RESERVED, KEY_LEFTALT, KEY_Z, KEY_S, KEY_A, KEY_W, KEY_2, KEY_F4,
+/* 20 */ KEY_RESERVED, KEY_C, KEY_X, KEY_D, KEY_E, KEY_4, KEY_3, KEY_F5,
+/* 28 */ KEY_RESERVED, KEY_SPACE, KEY_V, KEY_F, KEY_T, KEY_R, KEY_5, KEY_F6,
+/* 30 */ KEY_RESERVED, KEY_N, KEY_B, KEY_H, KEY_G, KEY_Y, KEY_6, KEY_F7,
+/* 38 */ KEY_RESERVED, KEY_RIGHTALT, KEY_M, KEY_J, KEY_U, KEY_7, KEY_8, KEY_F8,
+/* 40 */ KEY_RESERVED, KEY_COMMA, KEY_K, KEY_I, KEY_O, KEY_0, KEY_9, KEY_F9,
+/* 48 */ KEY_RESERVED, KEY_DOT, KEY_SLASH, KEY_L, KEY_SEMICOLON, KEY_P, KEY_MINUS, KEY_F10,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_APOSTROPHE,KEY_RESERVED, KEY_LEFTBRACE, KEY_EQUAL, KEY_F11, KEY_SYSRQ,
+/* 58 */ C_58, KEY_RIGHTSHIFT,KEY_ENTER, KEY_RIGHTBRACE,KEY_BACKSLASH, KEY_BACKSLASH,KEY_F12, KEY_SCROLLLOCK,
+/* 60 */ KEY_DOWN, C_61, KEY_PAUSE, KEY_UP, KEY_DELETE, KEY_END, KEY_BACKSPACE, KEY_INSERT,
+/* 68 */ KEY_RESERVED, KEY_KP1, KEY_RIGHT, KEY_KP4, KEY_KP7, KEY_PAGEDOWN, KEY_HOME, KEY_PAGEUP,
+/* 70 */ KEY_KP0, KEY_KPDOT, KEY_KP2, KEY_KP5, KEY_KP6, KEY_KP8, KEY_ESC, KEY_NUMLOCK,
+/* 78 */ KEY_F11, KEY_KPPLUS, KEY_KP3, KEY_KPMINUS, KEY_KPASTERISK,KEY_KP9, KEY_SCROLLLOCK,KEY_102ND,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RIGHTALT, 255, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_CAPSLOCK, KEY_RESERVED, KEY_LEFTMETA,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTMETA,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_COMPOSE,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPSLASH, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_KPENTER, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_END, KEY_RESERVED, KEY_LEFT, KEY_HOME, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_INSERT, KEY_DELETE, KEY_DOWN, KEY_RESERVED, KEY_RIGHT, KEY_UP, KEY_RESERVED, KEY_PAUSE,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_PAGEDOWN, KEY_RESERVED, KEY_SYSRQ, KEY_PAGEUP, KEY_RESERVED, KEY_RESERVED,
+
+/* These are offset for escaped keycodes: */
+
+/* 00 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_F7, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 08 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_LEFTMETA, KEY_RIGHTMETA, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 10 */ KEY_RESERVED, KEY_RIGHTALT, KEY_RESERVED, KEY_RESERVED, KEY_RIGHTCTRL, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 18 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 20 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 28 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 30 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 38 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 40 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 48 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 50 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 58 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 60 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 68 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 70 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 78 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 80 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 88 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 90 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* 98 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* a8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* b8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* c8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* d8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* e8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f0 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+/* f8 */ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
+
+#undef CONFLICT
+#undef C_07
+#undef C_11
+#undef C_14
+#undef C_58
+#undef C_61
+
diff --git a/drivers/input/keyboard/jornada680_kbd.c b/drivers/input/keyboard/jornada680_kbd.c
new file mode 100644
index 0000000..781fc61
--- /dev/null
+++ b/drivers/input/keyboard/jornada680_kbd.c
@@ -0,0 +1,279 @@
+/*
+ * drivers/input/keyboard/jornada680_kbd.c
+ *
+ * HP Jornada 620/660/680/690 scan keyboard platform driver
+ * Copyright (C) 2007 Kristoffer Ericson <Kristoffer.Ericson@gmail.com>
+ *
+ * Based on hp680_keyb.c
+ * Copyright (C) 2006 Paul Mundt
+ * Copyright (C) 2005 Andriy Skulysh
+ * Split from drivers/input/keyboard/hp600_keyb.c
+ * Copyright (C) 2000 Yaegashi Takeshi (hp6xx kbd scan routine and translation table)
+ * Copyright (C) 2000 Niibe Yutaka (HP620 Keyb translation table)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/input-polldev.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <asm/delay.h>
+#include <asm/io.h>
+
+#define PCCR 0xa4000104
+#define PDCR 0xa4000106
+#define PECR 0xa4000108
+#define PFCR 0xa400010a
+#define PCDR 0xa4000124
+#define PDDR 0xa4000126
+#define PEDR 0xa4000128
+#define PFDR 0xa400012a
+#define PGDR 0xa400012c
+#define PHDR 0xa400012e
+#define PJDR 0xa4000130
+#define PKDR 0xa4000132
+#define PLDR 0xa4000134
+
+static const unsigned short jornada_scancodes[] = {
+/* PTD1 */ KEY_CAPSLOCK, KEY_MACRO, KEY_LEFTCTRL, 0, KEY_ESC, KEY_KP5, 0, 0, /* 1 -> 8 */
+ KEY_F1, KEY_F2, KEY_F3, KEY_F8, KEY_F7, KEY_F6, KEY_F4, KEY_F5, /* 9 -> 16 */
+/* PTD5 */ KEY_SLASH, KEY_APOSTROPHE, KEY_ENTER, 0, KEY_Z, 0, 0, 0, /* 17 -> 24 */
+ KEY_X, KEY_C, KEY_V, KEY_DOT, KEY_COMMA, KEY_M, KEY_B, KEY_N, /* 25 -> 32 */
+/* PTD7 */ KEY_KP2, KEY_KP6, KEY_KP3, 0, 0, 0, 0, 0, /* 33 -> 40 */
+ KEY_F10, KEY_RO, KEY_F9, KEY_KP4, KEY_NUMLOCK, KEY_SCROLLLOCK, KEY_LEFTALT, KEY_HANJA, /* 41 -> 48 */
+/* PTE0 */ KEY_KATAKANA, KEY_KP0, KEY_GRAVE, 0, KEY_FINANCE, 0, 0, 0, /* 49 -> 56 */
+ KEY_KPMINUS, KEY_HIRAGANA, KEY_SPACE, KEY_KPDOT, KEY_VOLUMEUP, 249, 0, 0, /* 57 -> 64 */
+/* PTE1 */ KEY_SEMICOLON, KEY_RIGHTBRACE, KEY_BACKSLASH, 0, KEY_A, 0, 0, 0, /* 65 -> 72 */
+ KEY_S, KEY_D, KEY_F, KEY_L, KEY_K, KEY_J, KEY_G, KEY_H, /* 73 -> 80 */
+/* PTE3 */ KEY_KP8, KEY_LEFTMETA, KEY_RIGHTSHIFT, 0, KEY_TAB, 0, 0, 0, /* 81 -> 88 */
+ 0, KEY_LEFTSHIFT, KEY_KP7, KEY_KP9, KEY_KP1, KEY_F11, KEY_KPPLUS, KEY_KPASTERISK, /* 89 -> 96 */
+/* PTE6 */ KEY_P, KEY_LEFTBRACE, KEY_BACKSPACE, 0, KEY_Q, 0, 0, 0, /* 97 -> 104 */
+ KEY_W, KEY_E, KEY_R, KEY_O, KEY_I, KEY_U, KEY_T, KEY_Y, /* 105 -> 112 */
+/* PTE7 */ KEY_0, KEY_MINUS, KEY_EQUAL, 0, KEY_1, 0, 0, 0, /* 113 -> 120 */
+ KEY_2, KEY_3, KEY_4, KEY_9, KEY_8, KEY_7, KEY_5, KEY_6, /* 121 -> 128 */
+/* **** */ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0
+};
+
+#define JORNADA_SCAN_SIZE 18
+
+struct jornadakbd {
+ struct input_polled_dev *poll_dev;
+ unsigned short keymap[ARRAY_SIZE(jornada_scancodes)];
+ unsigned char length;
+ unsigned char old_scan[JORNADA_SCAN_SIZE];
+ unsigned char new_scan[JORNADA_SCAN_SIZE];
+};
+
+static void jornada_parse_kbd(struct jornadakbd *jornadakbd)
+{
+ struct input_dev *input_dev = jornadakbd->poll_dev->input;
+ unsigned short *keymap = jornadakbd->keymap;
+ unsigned int sync_me = 0;
+ unsigned int i, j;
+
+ for (i = 0; i < JORNADA_SCAN_SIZE; i++) {
+ unsigned char new = jornadakbd->new_scan[i];
+ unsigned char old = jornadakbd->old_scan[i];
+ unsigned int xor = new ^ old;
+
+ if (xor == 0)
+ continue;
+
+ for (j = 0; j < 8; j++) {
+ unsigned int bit = 1 << j;
+ if (xor & bit) {
+ unsigned int scancode = (i << 3) + j;
+ input_event(input_dev,
+ EV_MSC, MSC_SCAN, scancode);
+ input_report_key(input_dev,
+ keymap[scancode],
+ !(new & bit));
+ sync_me = 1;
+ }
+ }
+ }
+
+ if (sync_me)
+ input_sync(input_dev);
+}
+
+static void jornada_scan_keyb(unsigned char *s)
+{
+ int i;
+ unsigned short ec_static, dc_static; /* = UINT16_t */
+ unsigned char matrix_switch[] = {
+ 0xfd, 0xff, /* PTD1 PD(1) */
+ 0xdf, 0xff, /* PTD5 PD(5) */
+ 0x7f, 0xff, /* PTD7 PD(7) */
+ 0xff, 0xfe, /* PTE0 PE(0) */
+ 0xff, 0xfd, /* PTE1 PE(1) */
+ 0xff, 0xf7, /* PTE3 PE(3) */
+ 0xff, 0xbf, /* PTE6 PE(6) */
+ 0xff, 0x7f, /* PTE7 PE(7) */
+ }, *t = matrix_switch;
+ /* PD(x) :
+ 1. 0xcc0c & (1~(1 << (2*(x)+1)))))
+ 2. (0xf0cf & 0xfffff) */
+ /* PE(x) :
+ 1. 0xcc0c & 0xffff
+ 2. 0xf0cf & (1~(1 << (2*(x)+1))))) */
+ unsigned short matrix_PDE[] = {
+ 0xcc04, 0xf0cf, /* PD(1) */
+ 0xc40c, 0xf0cf, /* PD(5) */
+ 0x4c0c, 0xf0cf, /* PD(7) */
+ 0xcc0c, 0xf0cd, /* PE(0) */
+ 0xcc0c, 0xf0c7, /* PE(1) */
+ 0xcc0c, 0xf04f, /* PE(3) */
+ 0xcc0c, 0xd0cf, /* PE(6) */
+ 0xcc0c, 0x70cf, /* PE(7) */
+ }, *y = matrix_PDE;
+
+ /* Save these control reg bits */
+ dc_static = (ctrl_inw(PDCR) & (~0xcc0c));
+ ec_static = (ctrl_inw(PECR) & (~0xf0cf));
+
+ for (i = 0; i < 8; i++) {
+ /* disable output for all but the one we want to scan */
+ ctrl_outw((dc_static | *y++), PDCR);
+ ctrl_outw((ec_static | *y++), PECR);
+ udelay(5);
+
+ /* Get scanline row */
+ ctrl_outb(*t++, PDDR);
+ ctrl_outb(*t++, PEDR);
+ udelay(50);
+
+ /* Read data */
+ *s++ = ctrl_inb(PCDR);
+ *s++ = ctrl_inb(PFDR);
+ }
+ /* Scan no lines */
+ ctrl_outb(0xff, PDDR);
+ ctrl_outb(0xff, PEDR);
+
+ /* Enable all scanlines */
+ ctrl_outw((dc_static | (0x5555 & 0xcc0c)),PDCR);
+ ctrl_outw((ec_static | (0x5555 & 0xf0cf)),PECR);
+
+ /* Ignore extra keys and events */
+ *s++ = ctrl_inb(PGDR);
+ *s++ = ctrl_inb(PHDR);
+}
+
+static void jornadakbd680_poll(struct input_polled_dev *dev)
+{
+ struct jornadakbd *jornadakbd = dev->private;
+
+ jornada_scan_keyb(jornadakbd->new_scan);
+ jornada_parse_kbd(jornadakbd);
+ memcpy(jornadakbd->old_scan, jornadakbd->new_scan, JORNADA_SCAN_SIZE);
+}
+
+static int __devinit jornada680kbd_probe(struct platform_device *pdev)
+{
+ struct jornadakbd *jornadakbd;
+ struct input_polled_dev *poll_dev;
+ struct input_dev *input_dev;
+ int i, error;
+
+ jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
+ if (!jornadakbd)
+ return -ENOMEM;
+
+ poll_dev = input_allocate_polled_device();
+ if (!poll_dev) {
+ error = -ENOMEM;
+ goto failed;
+ }
+
+ platform_set_drvdata(pdev, jornadakbd);
+
+ jornadakbd->poll_dev = poll_dev;
+
+ memcpy(jornadakbd->keymap, jornada_scancodes,
+ sizeof(jornadakbd->keymap));
+
+ poll_dev->private = jornadakbd;
+ poll_dev->poll = jornadakbd680_poll;
+ poll_dev->poll_interval = 50; /* msec */
+
+ input_dev = poll_dev->input;
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->name = "HP Jornada 680 keyboard";
+ input_dev->phys = "jornadakbd/input0";
+ input_dev->keycode = jornadakbd->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(jornada_scancodes);
+ input_dev->dev.parent = &pdev->dev;
+ input_dev->id.bustype = BUS_HOST;
+
+ for (i = 0; i < 128; i++)
+ if (jornadakbd->keymap[i])
+ __set_bit(jornadakbd->keymap[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ error = input_register_polled_device(jornadakbd->poll_dev);
+ if (error)
+ goto failed;
+
+ return 0;
+
+ failed:
+ printk(KERN_ERR "Jornadakbd: failed to register driver, error: %d\n",
+ error);
+ platform_set_drvdata(pdev, NULL);
+ input_free_polled_device(poll_dev);
+ kfree(jornadakbd);
+ return error;
+
+}
+
+static int __devexit jornada680kbd_remove(struct platform_device *pdev)
+{
+ struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_polled_device(jornadakbd->poll_dev);
+ input_free_polled_device(jornadakbd->poll_dev);
+ kfree(jornadakbd);
+
+ return 0;
+}
+
+static struct platform_driver jornada680kbd_driver = {
+ .driver = {
+ .name = "jornada680_kbd",
+ .owner = THIS_MODULE,
+ },
+ .probe = jornada680kbd_probe,
+ .remove = __devexit_p(jornada680kbd_remove),
+};
+
+static int __init jornada680kbd_init(void)
+{
+ return platform_driver_register(&jornada680kbd_driver);
+}
+
+static void __exit jornada680kbd_exit(void)
+{
+ platform_driver_unregister(&jornada680kbd_driver);
+}
+
+module_init(jornada680kbd_init);
+module_exit(jornada680kbd_exit);
+
+MODULE_AUTHOR("Kristoffer Ericson <kristoffer.ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 620/660/680/690 Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:jornada680_kbd");
diff --git a/drivers/input/keyboard/jornada720_kbd.c b/drivers/input/keyboard/jornada720_kbd.c
new file mode 100644
index 0000000..4e016d8
--- /dev/null
+++ b/drivers/input/keyboard/jornada720_kbd.c
@@ -0,0 +1,188 @@
+/*
+ * drivers/input/keyboard/jornada720_kbd.c
+ *
+ * HP Jornada 720 keyboard platform driver
+ *
+ * Copyright (C) 2006/2007 Kristoffer Ericson <Kristoffer.Ericson@Gmail.com>
+ *
+ * Copyright (C) 2006 jornada 720 kbd driver by
+ Filip Zyzniewsk <Filip.Zyzniewski@tefnet.plX
+ * based on (C) 2004 jornada 720 kbd driver by
+ Alex Lange <chicken@handhelds.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/device.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+
+#include <mach/jornada720.h>
+#include <mach/hardware.h>
+
+MODULE_AUTHOR("Kristoffer Ericson <Kristoffer.Ericson@gmail.com>");
+MODULE_DESCRIPTION("HP Jornada 710/720/728 keyboard driver");
+MODULE_LICENSE("GPL v2");
+
+static unsigned short jornada_std_keymap[128] = { /* ROW */
+ 0, KEY_ESC, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7, /* #1 */
+ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE, /* -> */
+ 0, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6, KEY_7, KEY_8, KEY_9, /* #2 */
+ KEY_0, KEY_MINUS, KEY_EQUAL,0, 0, 0, /* -> */
+ 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_U, KEY_I, KEY_O, /* #3 */
+ KEY_P, KEY_BACKSLASH, KEY_BACKSPACE, 0, 0, 0, /* -> */
+ 0, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_J, KEY_K, KEY_L, /* #4 */
+ KEY_SEMICOLON, KEY_LEFTBRACE, KEY_RIGHTBRACE, 0, 0, 0, /* -> */
+ 0, KEY_Z, KEY_X, KEY_C, KEY_V, KEY_B, KEY_N, KEY_M, KEY_COMMA, /* #5 */
+ KEY_DOT, KEY_KPMINUS, KEY_APOSTROPHE, KEY_ENTER, 0, 0,0, /* -> */
+ 0, KEY_TAB, 0, KEY_LEFTSHIFT, 0, KEY_APOSTROPHE, 0, 0, 0, 0, /* #6 */
+ KEY_UP, 0, KEY_RIGHTSHIFT, 0, 0, 0,0, 0, 0, 0, 0, KEY_LEFTALT, KEY_GRAVE, /* -> */
+ 0, 0, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0,0, KEY_KPASTERISK, /* -> */
+ KEY_LEFTCTRL, 0, KEY_SPACE, 0, 0, 0, KEY_SLASH, KEY_DELETE, 0, 0, /* -> */
+ 0, 0, 0, KEY_POWER, /* -> */
+};
+
+struct jornadakbd {
+ unsigned short keymap[ARRAY_SIZE(jornada_std_keymap)];
+ struct input_dev *input;
+};
+
+static irqreturn_t jornada720_kbd_interrupt(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
+ struct input_dev *input = jornadakbd->input;
+ u8 count, kbd_data, scan_code;
+
+ /* startup ssp with spinlock */
+ jornada_ssp_start();
+
+ if (jornada_ssp_inout(GETSCANKEYCODE) != TXDUMMY) {
+ printk(KERN_DEBUG
+ "jornada720_kbd: "
+ "GetKeycode command failed with ETIMEDOUT, "
+ "flushed bus\n");
+ } else {
+ /* How many keycodes are waiting for us? */
+ count = jornada_ssp_byte(TXDUMMY);
+
+ /* Lets drag them out one at a time */
+ while (count--) {
+ /* Exchange TxDummy for location (keymap[kbddata]) */
+ kbd_data = jornada_ssp_byte(TXDUMMY);
+ scan_code = kbd_data & 0x7f;
+
+ input_event(input, EV_MSC, MSC_SCAN, scan_code);
+ input_report_key(input, jornadakbd->keymap[scan_code],
+ !(kbd_data & 0x80));
+ input_sync(input);
+ }
+ }
+
+ /* release spinlock and turn off ssp */
+ jornada_ssp_end();
+
+ return IRQ_HANDLED;
+};
+
+static int __devinit jornada720_kbd_probe(struct platform_device *pdev)
+{
+ struct jornadakbd *jornadakbd;
+ struct input_dev *input_dev;
+ int i, err;
+
+ jornadakbd = kzalloc(sizeof(struct jornadakbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!jornadakbd || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ platform_set_drvdata(pdev, jornadakbd);
+
+ memcpy(jornadakbd->keymap, jornada_std_keymap,
+ sizeof(jornada_std_keymap));
+ jornadakbd->input = input_dev;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->name = "HP Jornada 720 keyboard";
+ input_dev->phys = "jornadakbd/input0";
+ input_dev->keycode = jornadakbd->keymap;
+ input_dev->keycodesize = sizeof(unsigned short);
+ input_dev->keycodemax = ARRAY_SIZE(jornada_std_keymap);
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->dev.parent = &pdev->dev;
+
+ for (i = 0; i < ARRAY_SIZE(jornadakbd->keymap); i++)
+ __set_bit(jornadakbd->keymap[i], input_dev->keybit);
+ __clear_bit(KEY_RESERVED, input_dev->keybit);
+
+ input_set_capability(input_dev, EV_MSC, MSC_SCAN);
+
+ err = request_irq(IRQ_GPIO0,
+ jornada720_kbd_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_FALLING,
+ "jornadakbd", pdev);
+ if (err) {
+ printk(KERN_INFO "jornadakbd720_kbd: Unable to grab IRQ\n");
+ goto fail1;
+ }
+
+ err = input_register_device(jornadakbd->input);
+ if (err)
+ goto fail2;
+
+ return 0;
+
+ fail2: /* IRQ, DEVICE, MEMORY */
+ free_irq(IRQ_GPIO0, pdev);
+ fail1: /* DEVICE, MEMORY */
+ platform_set_drvdata(pdev, NULL);
+ input_free_device(input_dev);
+ kfree(jornadakbd);
+ return err;
+};
+
+static int __devexit jornada720_kbd_remove(struct platform_device *pdev)
+{
+ struct jornadakbd *jornadakbd = platform_get_drvdata(pdev);
+
+ free_irq(IRQ_GPIO0, pdev);
+ platform_set_drvdata(pdev, NULL);
+ input_unregister_device(jornadakbd->input);
+ kfree(jornadakbd);
+
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:jornada720_kbd");
+
+static struct platform_driver jornada720_kbd_driver = {
+ .driver = {
+ .name = "jornada720_kbd",
+ .owner = THIS_MODULE,
+ },
+ .probe = jornada720_kbd_probe,
+ .remove = __devexit_p(jornada720_kbd_remove),
+};
+
+static int __init jornada720_kbd_init(void)
+{
+ return platform_driver_register(&jornada720_kbd_driver);
+}
+
+static void __exit jornada720_kbd_exit(void)
+{
+ platform_driver_unregister(&jornada720_kbd_driver);
+}
+
+module_init(jornada720_kbd_init);
+module_exit(jornada720_kbd_exit);
diff --git a/drivers/input/keyboard/lkkbd.c b/drivers/input/keyboard/lkkbd.c
new file mode 100644
index 0000000..4730ef3
--- /dev/null
+++ b/drivers/input/keyboard/lkkbd.c
@@ -0,0 +1,767 @@
+/*
+ * Copyright (C) 2004 by Jan-Benedict Glaw <jbglaw@lug-owl.de>
+ */
+
+/*
+ * LK keyboard driver for Linux, based on sunkbd.c (C) by Vojtech Pavlik
+ */
+
+/*
+ * DEC LK201 and LK401 keyboard driver for Linux (primary for DECstations
+ * and VAXstations, but can also be used on any standard RS232 with an
+ * adaptor).
+ *
+ * DISCLAIMER: This works for _me_. If you break anything by using the
+ * information given below, I will _not_ be liable!
+ *
+ * RJ10 pinout: To DE9: Or DB25:
+ * 1 - RxD <----> Pin 3 (TxD) <-> Pin 2 (TxD)
+ * 2 - GND <----> Pin 5 (GND) <-> Pin 7 (GND)
+ * 4 - TxD <----> Pin 2 (RxD) <-> Pin 3 (RxD)
+ * 3 - +12V (from HDD drive connector), DON'T connect to DE9 or DB25!!!
+ *
+ * Pin numbers for DE9 and DB25 are noted on the plug (quite small:). For
+ * RJ10, it's like this:
+ *
+ * __=__ Hold the plug in front of you, cable downwards,
+ * /___/| nose is hidden behind the plug. Now, pin 1 is at
+ * |1234|| the left side, pin 4 at the right and 2 and 3 are
+ * |IIII|| in between, of course:)
+ * | ||
+ * |____|/
+ * || So the adaptor consists of three connected cables
+ * || for data transmission (RxD and TxD) and signal ground.
+ * Additionally, you have to get +12V from somewhere.
+ * Most easily, you'll get that from a floppy or HDD power connector.
+ * It's the yellow cable there (black is ground and red is +5V).
+ *
+ * The keyboard and all the commands it understands are documented in
+ * "VCB02 Video Subsystem - Technical Manual", EK-104AA-TM-001. This
+ * document is LK201 specific, but LK401 is mostly compatible. It comes
+ * up in LK201 mode and doesn't report any of the additional keys it
+ * has. These need to be switched on with the LK_CMD_ENABLE_LK401
+ * command. You'll find this document (scanned .pdf file) on MANX,
+ * a search engine specific to DEC documentation. Try
+ * http://www.vt100.net/manx/details?pn=EK-104AA-TM-001;id=21;cp=1
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC "LK keyboard driver"
+
+MODULE_AUTHOR ("Jan-Benedict Glaw <jbglaw@lug-owl.de>");
+MODULE_DESCRIPTION (DRIVER_DESC);
+MODULE_LICENSE ("GPL");
+
+/*
+ * Known parameters:
+ * bell_volume
+ * keyclick_volume
+ * ctrlclick_volume
+ *
+ * Please notice that there's not yet an API to set these at runtime.
+ */
+static int bell_volume = 100; /* % */
+module_param (bell_volume, int, 0);
+MODULE_PARM_DESC (bell_volume, "Bell volume (in %). default is 100%");
+
+static int keyclick_volume = 100; /* % */
+module_param (keyclick_volume, int, 0);
+MODULE_PARM_DESC (keyclick_volume, "Keyclick volume (in %), default is 100%");
+
+static int ctrlclick_volume = 100; /* % */
+module_param (ctrlclick_volume, int, 0);
+MODULE_PARM_DESC (ctrlclick_volume, "Ctrlclick volume (in %), default is 100%");
+
+static int lk201_compose_is_alt;
+module_param (lk201_compose_is_alt, int, 0);
+MODULE_PARM_DESC (lk201_compose_is_alt, "If set non-zero, LK201' Compose key "
+ "will act as an Alt key");
+
+
+
+#undef LKKBD_DEBUG
+#ifdef LKKBD_DEBUG
+#define DBG(x...) printk (x)
+#else
+#define DBG(x...) do {} while (0)
+#endif
+
+/* LED control */
+#define LK_LED_WAIT 0x81
+#define LK_LED_COMPOSE 0x82
+#define LK_LED_SHIFTLOCK 0x84
+#define LK_LED_SCROLLLOCK 0x88
+#define LK_CMD_LED_ON 0x13
+#define LK_CMD_LED_OFF 0x11
+
+/* Mode control */
+#define LK_MODE_DOWN 0x80
+#define LK_MODE_AUTODOWN 0x82
+#define LK_MODE_UPDOWN 0x86
+#define LK_CMD_SET_MODE(mode,div) ((mode) | ((div) << 3))
+
+/* Misc commands */
+#define LK_CMD_ENABLE_KEYCLICK 0x1b
+#define LK_CMD_DISABLE_KEYCLICK 0x99
+#define LK_CMD_DISABLE_BELL 0xa1
+#define LK_CMD_SOUND_BELL 0xa7
+#define LK_CMD_ENABLE_BELL 0x23
+#define LK_CMD_DISABLE_CTRCLICK 0xb9
+#define LK_CMD_ENABLE_CTRCLICK 0xbb
+#define LK_CMD_SET_DEFAULTS 0xd3
+#define LK_CMD_POWERCYCLE_RESET 0xfd
+#define LK_CMD_ENABLE_LK401 0xe9
+#define LK_CMD_REQUEST_ID 0xab
+
+/* Misc responses from keyboard */
+#define LK_STUCK_KEY 0x3d
+#define LK_SELFTEST_FAILED 0x3e
+#define LK_ALL_KEYS_UP 0xb3
+#define LK_METRONOME 0xb4
+#define LK_OUTPUT_ERROR 0xb5
+#define LK_INPUT_ERROR 0xb6
+#define LK_KBD_LOCKED 0xb7
+#define LK_KBD_TEST_MODE_ACK 0xb8
+#define LK_PREFIX_KEY_DOWN 0xb9
+#define LK_MODE_CHANGE_ACK 0xba
+#define LK_RESPONSE_RESERVED 0xbb
+
+#define LK_NUM_KEYCODES 256
+#define LK_NUM_IGNORE_BYTES 6
+typedef u_int16_t lk_keycode_t;
+
+
+
+static lk_keycode_t lkkbd_keycode[LK_NUM_KEYCODES] = {
+ [0x56] = KEY_F1,
+ [0x57] = KEY_F2,
+ [0x58] = KEY_F3,
+ [0x59] = KEY_F4,
+ [0x5a] = KEY_F5,
+ [0x64] = KEY_F6,
+ [0x65] = KEY_F7,
+ [0x66] = KEY_F8,
+ [0x67] = KEY_F9,
+ [0x68] = KEY_F10,
+ [0x71] = KEY_F11,
+ [0x72] = KEY_F12,
+ [0x73] = KEY_F13,
+ [0x74] = KEY_F14,
+ [0x7c] = KEY_F15,
+ [0x7d] = KEY_F16,
+ [0x80] = KEY_F17,
+ [0x81] = KEY_F18,
+ [0x82] = KEY_F19,
+ [0x83] = KEY_F20,
+ [0x8a] = KEY_FIND,
+ [0x8b] = KEY_INSERT,
+ [0x8c] = KEY_DELETE,
+ [0x8d] = KEY_SELECT,
+ [0x8e] = KEY_PAGEUP,
+ [0x8f] = KEY_PAGEDOWN,
+ [0x92] = KEY_KP0,
+ [0x94] = KEY_KPDOT,
+ [0x95] = KEY_KPENTER,
+ [0x96] = KEY_KP1,
+ [0x97] = KEY_KP2,
+ [0x98] = KEY_KP3,
+ [0x99] = KEY_KP4,
+ [0x9a] = KEY_KP5,
+ [0x9b] = KEY_KP6,
+ [0x9c] = KEY_KPCOMMA,
+ [0x9d] = KEY_KP7,
+ [0x9e] = KEY_KP8,
+ [0x9f] = KEY_KP9,
+ [0xa0] = KEY_KPMINUS,
+ [0xa1] = KEY_PROG1,
+ [0xa2] = KEY_PROG2,
+ [0xa3] = KEY_PROG3,
+ [0xa4] = KEY_PROG4,
+ [0xa7] = KEY_LEFT,
+ [0xa8] = KEY_RIGHT,
+ [0xa9] = KEY_DOWN,
+ [0xaa] = KEY_UP,
+ [0xab] = KEY_RIGHTSHIFT,
+ [0xac] = KEY_LEFTALT,
+ [0xad] = KEY_COMPOSE, /* Right Compose, that is. */
+ [0xae] = KEY_LEFTSHIFT, /* Same as KEY_RIGHTSHIFT on LK201 */
+ [0xaf] = KEY_LEFTCTRL,
+ [0xb0] = KEY_CAPSLOCK,
+ [0xb1] = KEY_COMPOSE, /* Left Compose, that is. */
+ [0xb2] = KEY_RIGHTALT,
+ [0xbc] = KEY_BACKSPACE,
+ [0xbd] = KEY_ENTER,
+ [0xbe] = KEY_TAB,
+ [0xbf] = KEY_ESC,
+ [0xc0] = KEY_1,
+ [0xc1] = KEY_Q,
+ [0xc2] = KEY_A,
+ [0xc3] = KEY_Z,
+ [0xc5] = KEY_2,
+ [0xc6] = KEY_W,
+ [0xc7] = KEY_S,
+ [0xc8] = KEY_X,
+ [0xc9] = KEY_102ND,
+ [0xcb] = KEY_3,
+ [0xcc] = KEY_E,
+ [0xcd] = KEY_D,
+ [0xce] = KEY_C,
+ [0xd0] = KEY_4,
+ [0xd1] = KEY_R,
+ [0xd2] = KEY_F,
+ [0xd3] = KEY_V,
+ [0xd4] = KEY_SPACE,
+ [0xd6] = KEY_5,
+ [0xd7] = KEY_T,
+ [0xd8] = KEY_G,
+ [0xd9] = KEY_B,
+ [0xdb] = KEY_6,
+ [0xdc] = KEY_Y,
+ [0xdd] = KEY_H,
+ [0xde] = KEY_N,
+ [0xe0] = KEY_7,
+ [0xe1] = KEY_U,
+ [0xe2] = KEY_J,
+ [0xe3] = KEY_M,
+ [0xe5] = KEY_8,
+ [0xe6] = KEY_I,
+ [0xe7] = KEY_K,
+ [0xe8] = KEY_COMMA,
+ [0xea] = KEY_9,
+ [0xeb] = KEY_O,
+ [0xec] = KEY_L,
+ [0xed] = KEY_DOT,
+ [0xef] = KEY_0,
+ [0xf0] = KEY_P,
+ [0xf2] = KEY_SEMICOLON,
+ [0xf3] = KEY_SLASH,
+ [0xf5] = KEY_EQUAL,
+ [0xf6] = KEY_RIGHTBRACE,
+ [0xf7] = KEY_BACKSLASH,
+ [0xf9] = KEY_MINUS,
+ [0xfa] = KEY_LEFTBRACE,
+ [0xfb] = KEY_APOSTROPHE,
+};
+
+#define CHECK_LED(LK, VAR_ON, VAR_OFF, LED, BITS) do { \
+ if (test_bit (LED, (LK)->dev->led)) \
+ VAR_ON |= BITS; \
+ else \
+ VAR_OFF |= BITS; \
+ } while (0)
+
+/*
+ * Per-keyboard data
+ */
+struct lkkbd {
+ lk_keycode_t keycode[LK_NUM_KEYCODES];
+ int ignore_bytes;
+ unsigned char id[LK_NUM_IGNORE_BYTES];
+ struct input_dev *dev;
+ struct serio *serio;
+ struct work_struct tq;
+ char name[64];
+ char phys[32];
+ char type;
+ int bell_volume;
+ int keyclick_volume;
+ int ctrlclick_volume;
+};
+
+#ifdef LKKBD_DEBUG
+/*
+ * Responses from the keyboard and mapping back to their names.
+ */
+static struct {
+ unsigned char value;
+ unsigned char *name;
+} lk_response[] = {
+#define RESPONSE(x) { .value = (x), .name = #x, }
+ RESPONSE (LK_STUCK_KEY),
+ RESPONSE (LK_SELFTEST_FAILED),
+ RESPONSE (LK_ALL_KEYS_UP),
+ RESPONSE (LK_METRONOME),
+ RESPONSE (LK_OUTPUT_ERROR),
+ RESPONSE (LK_INPUT_ERROR),
+ RESPONSE (LK_KBD_LOCKED),
+ RESPONSE (LK_KBD_TEST_MODE_ACK),
+ RESPONSE (LK_PREFIX_KEY_DOWN),
+ RESPONSE (LK_MODE_CHANGE_ACK),
+ RESPONSE (LK_RESPONSE_RESERVED),
+#undef RESPONSE
+};
+
+static unsigned char *
+response_name (unsigned char value)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE (lk_response); i++)
+ if (lk_response[i].value == value)
+ return lk_response[i].name;
+
+ return "<unknown>";
+}
+#endif /* LKKBD_DEBUG */
+
+/*
+ * Calculate volume parameter byte for a given volume.
+ */
+static unsigned char
+volume_to_hw (int volume_percent)
+{
+ unsigned char ret = 0;
+
+ if (volume_percent < 0)
+ volume_percent = 0;
+ if (volume_percent > 100)
+ volume_percent = 100;
+
+ if (volume_percent >= 0)
+ ret = 7;
+ if (volume_percent >= 13) /* 12.5 */
+ ret = 6;
+ if (volume_percent >= 25)
+ ret = 5;
+ if (volume_percent >= 38) /* 37.5 */
+ ret = 4;
+ if (volume_percent >= 50)
+ ret = 3;
+ if (volume_percent >= 63) /* 62.5 */
+ ret = 2; /* This is the default volume */
+ if (volume_percent >= 75)
+ ret = 1;
+ if (volume_percent >= 88) /* 87.5 */
+ ret = 0;
+
+ ret |= 0x80;
+
+ return ret;
+}
+
+static void
+lkkbd_detection_done (struct lkkbd *lk)
+{
+ int i;
+
+ /*
+ * Reset setting for Compose key. Let Compose be KEY_COMPOSE.
+ */
+ lk->keycode[0xb1] = KEY_COMPOSE;
+
+ /*
+ * Print keyboard name and modify Compose=Alt on user's request.
+ */
+ switch (lk->id[4]) {
+ case 1:
+ strlcpy (lk->name, "DEC LK201 keyboard",
+ sizeof (lk->name));
+
+ if (lk201_compose_is_alt)
+ lk->keycode[0xb1] = KEY_LEFTALT;
+ break;
+
+ case 2:
+ strlcpy (lk->name, "DEC LK401 keyboard",
+ sizeof (lk->name));
+ break;
+
+ default:
+ strlcpy (lk->name, "Unknown DEC keyboard",
+ sizeof (lk->name));
+ printk (KERN_ERR "lkkbd: keyboard on %s is unknown, "
+ "please report to Jan-Benedict Glaw "
+ "<jbglaw@lug-owl.de>\n", lk->phys);
+ printk (KERN_ERR "lkkbd: keyboard ID'ed as:");
+ for (i = 0; i < LK_NUM_IGNORE_BYTES; i++)
+ printk (" 0x%02x", lk->id[i]);
+ printk ("\n");
+ break;
+ }
+ printk (KERN_INFO "lkkbd: keyboard on %s identified as: %s\n",
+ lk->phys, lk->name);
+
+ /*
+ * Report errors during keyboard boot-up.
+ */
+ switch (lk->id[2]) {
+ case 0x00:
+ /* All okay */
+ break;
+
+ case LK_STUCK_KEY:
+ printk (KERN_ERR "lkkbd: Stuck key on keyboard at "
+ "%s\n", lk->phys);
+ break;
+
+ case LK_SELFTEST_FAILED:
+ printk (KERN_ERR "lkkbd: Selftest failed on keyboard "
+ "at %s, keyboard may not work "
+ "properly\n", lk->phys);
+ break;
+
+ default:
+ printk (KERN_ERR "lkkbd: Unknown error %02x on "
+ "keyboard at %s\n", lk->id[2],
+ lk->phys);
+ break;
+ }
+
+ /*
+ * Try to hint user if there's a stuck key.
+ */
+ if (lk->id[2] == LK_STUCK_KEY && lk->id[3] != 0)
+ printk (KERN_ERR "Scancode of stuck key is 0x%02x, keycode "
+ "is 0x%04x\n", lk->id[3],
+ lk->keycode[lk->id[3]]);
+
+ return;
+}
+
+/*
+ * lkkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+static irqreturn_t
+lkkbd_interrupt (struct serio *serio, unsigned char data, unsigned int flags)
+{
+ struct lkkbd *lk = serio_get_drvdata (serio);
+ int i;
+
+ DBG (KERN_INFO "Got byte 0x%02x\n", data);
+
+ if (lk->ignore_bytes > 0) {
+ DBG (KERN_INFO "Ignoring a byte on %s\n", lk->name);
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+
+ if (lk->ignore_bytes == 0)
+ lkkbd_detection_done (lk);
+
+ return IRQ_HANDLED;
+ }
+
+ switch (data) {
+ case LK_ALL_KEYS_UP:
+ for (i = 0; i < ARRAY_SIZE (lkkbd_keycode); i++)
+ if (lk->keycode[i] != KEY_RESERVED)
+ input_report_key (lk->dev, lk->keycode[i], 0);
+ input_sync (lk->dev);
+ break;
+
+ case 0x01:
+ DBG (KERN_INFO "Got 0x01, scheduling re-initialization\n");
+ lk->ignore_bytes = LK_NUM_IGNORE_BYTES;
+ lk->id[LK_NUM_IGNORE_BYTES - lk->ignore_bytes--] = data;
+ schedule_work (&lk->tq);
+ break;
+
+ case LK_METRONOME:
+ case LK_OUTPUT_ERROR:
+ case LK_INPUT_ERROR:
+ case LK_KBD_LOCKED:
+ case LK_KBD_TEST_MODE_ACK:
+ case LK_PREFIX_KEY_DOWN:
+ case LK_MODE_CHANGE_ACK:
+ case LK_RESPONSE_RESERVED:
+ DBG (KERN_INFO "Got %s and don't know how to handle...\n",
+ response_name (data));
+ break;
+
+ default:
+ if (lk->keycode[data] != KEY_RESERVED) {
+ if (!test_bit (lk->keycode[data], lk->dev->key))
+ input_report_key (lk->dev, lk->keycode[data], 1);
+ else
+ input_report_key (lk->dev, lk->keycode[data], 0);
+ input_sync (lk->dev);
+ } else
+ printk (KERN_WARNING "%s: Unknown key with "
+ "scancode 0x%02x on %s.\n",
+ __FILE__, data, lk->name);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * lkkbd_event() handles events from the input module.
+ */
+static int
+lkkbd_event (struct input_dev *dev, unsigned int type, unsigned int code,
+ int value)
+{
+ struct lkkbd *lk = input_get_drvdata (dev);
+ unsigned char leds_on = 0;
+ unsigned char leds_off = 0;
+
+ switch (type) {
+ case EV_LED:
+ CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
+ CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
+ CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
+ CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
+ if (leds_on != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_ON);
+ lk->serio->write (lk->serio, leds_on);
+ }
+ if (leds_off != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_OFF);
+ lk->serio->write (lk->serio, leds_off);
+ }
+ return 0;
+
+ case EV_SND:
+ switch (code) {
+ case SND_CLICK:
+ if (value == 0) {
+ DBG ("%s: Deactivating key clicks\n", __func__);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ } else {
+ DBG ("%s: Activating key clicks\n", __func__);
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ }
+ return 0;
+
+ case SND_BELL:
+ if (value != 0)
+ lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+
+ return 0;
+ }
+ break;
+
+ default:
+ printk (KERN_ERR "%s (): Got unknown type %d, code %d, value %d\n",
+ __func__, type, code, value);
+ }
+
+ return -1;
+}
+
+/*
+ * lkkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+static void
+lkkbd_reinit (struct work_struct *work)
+{
+ struct lkkbd *lk = container_of(work, struct lkkbd, tq);
+ int division;
+ unsigned char leds_on = 0;
+ unsigned char leds_off = 0;
+
+ /* Ask for ID */
+ lk->serio->write (lk->serio, LK_CMD_REQUEST_ID);
+
+ /* Reset parameters */
+ lk->serio->write (lk->serio, LK_CMD_SET_DEFAULTS);
+
+ /* Set LEDs */
+ CHECK_LED (lk, leds_on, leds_off, LED_CAPSL, LK_LED_SHIFTLOCK);
+ CHECK_LED (lk, leds_on, leds_off, LED_COMPOSE, LK_LED_COMPOSE);
+ CHECK_LED (lk, leds_on, leds_off, LED_SCROLLL, LK_LED_SCROLLLOCK);
+ CHECK_LED (lk, leds_on, leds_off, LED_SLEEP, LK_LED_WAIT);
+ if (leds_on != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_ON);
+ lk->serio->write (lk->serio, leds_on);
+ }
+ if (leds_off != 0) {
+ lk->serio->write (lk->serio, LK_CMD_LED_OFF);
+ lk->serio->write (lk->serio, leds_off);
+ }
+
+ /*
+ * Try to activate extended LK401 mode. This command will
+ * only work with a LK401 keyboard and grants access to
+ * LAlt, RAlt, RCompose and RShift.
+ */
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_LK401);
+
+ /* Set all keys to UPDOWN mode */
+ for (division = 1; division <= 14; division++)
+ lk->serio->write (lk->serio, LK_CMD_SET_MODE (LK_MODE_UPDOWN,
+ division));
+
+ /* Enable bell and set volume */
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_BELL);
+ lk->serio->write (lk->serio, volume_to_hw (lk->bell_volume));
+
+ /* Enable/disable keyclick (and possibly set volume) */
+ if (test_bit (SND_CLICK, lk->dev->snd)) {
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_KEYCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->keyclick_volume));
+ lk->serio->write (lk->serio, LK_CMD_ENABLE_CTRCLICK);
+ lk->serio->write (lk->serio, volume_to_hw (lk->ctrlclick_volume));
+ } else {
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_KEYCLICK);
+ lk->serio->write (lk->serio, LK_CMD_DISABLE_CTRCLICK);
+ }
+
+ /* Sound the bell if needed */
+ if (test_bit (SND_BELL, lk->dev->snd))
+ lk->serio->write (lk->serio, LK_CMD_SOUND_BELL);
+}
+
+/*
+ * lkkbd_connect() probes for a LK keyboard and fills the necessary structures.
+ */
+static int
+lkkbd_connect (struct serio *serio, struct serio_driver *drv)
+{
+ struct lkkbd *lk;
+ struct input_dev *input_dev;
+ int i;
+ int err;
+
+ lk = kzalloc (sizeof (struct lkkbd), GFP_KERNEL);
+ input_dev = input_allocate_device ();
+ if (!lk || !input_dev) {
+ err = -ENOMEM;
+ goto fail1;
+ }
+
+ lk->serio = serio;
+ lk->dev = input_dev;
+ INIT_WORK (&lk->tq, lkkbd_reinit);
+ lk->bell_volume = bell_volume;
+ lk->keyclick_volume = keyclick_volume;
+ lk->ctrlclick_volume = ctrlclick_volume;
+ memcpy (lk->keycode, lkkbd_keycode, sizeof (lk_keycode_t) * LK_NUM_KEYCODES);
+
+ strlcpy (lk->name, "DEC LK keyboard", sizeof(lk->name));
+ snprintf (lk->phys, sizeof(lk->phys), "%s/input0", serio->phys);
+
+ input_dev->name = lk->name;
+ input_dev->phys = lk->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_LKKBD;
+ input_dev->id.product = 0;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+ input_dev->event = lkkbd_event;
+
+ input_set_drvdata (input_dev, lk);
+
+ set_bit (EV_KEY, input_dev->evbit);
+ set_bit (EV_LED, input_dev->evbit);
+ set_bit (EV_SND, input_dev->evbit);
+ set_bit (EV_REP, input_dev->evbit);
+ set_bit (LED_CAPSL, input_dev->ledbit);
+ set_bit (LED_SLEEP, input_dev->ledbit);
+ set_bit (LED_COMPOSE, input_dev->ledbit);
+ set_bit (LED_SCROLLL, input_dev->ledbit);
+ set_bit (SND_BELL, input_dev->sndbit);
+ set_bit (SND_CLICK, input_dev->sndbit);
+
+ input_dev->keycode = lk->keycode;
+ input_dev->keycodesize = sizeof (lk_keycode_t);
+ input_dev->keycodemax = LK_NUM_KEYCODES;
+ for (i = 0; i < LK_NUM_KEYCODES; i++)
+ set_bit (lk->keycode[i], input_dev->keybit);
+
+ serio_set_drvdata (serio, lk);
+
+ err = serio_open (serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device (lk->dev);
+ if (err)
+ goto fail3;
+
+ lk->serio->write (lk->serio, LK_CMD_POWERCYCLE_RESET);
+
+ return 0;
+
+ fail3: serio_close (serio);
+ fail2: serio_set_drvdata (serio, NULL);
+ fail1: input_free_device (input_dev);
+ kfree (lk);
+ return err;
+}
+
+/*
+ * lkkbd_disconnect() unregisters and closes behind us.
+ */
+static void
+lkkbd_disconnect (struct serio *serio)
+{
+ struct lkkbd *lk = serio_get_drvdata (serio);
+
+ input_get_device (lk->dev);
+ input_unregister_device (lk->dev);
+ serio_close (serio);
+ serio_set_drvdata (serio, NULL);
+ input_put_device (lk->dev);
+ kfree (lk);
+}
+
+static struct serio_device_id lkkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_LKKBD,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, lkkbd_serio_ids);
+
+static struct serio_driver lkkbd_drv = {
+ .driver = {
+ .name = "lkkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = lkkbd_serio_ids,
+ .connect = lkkbd_connect,
+ .disconnect = lkkbd_disconnect,
+ .interrupt = lkkbd_interrupt,
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+static int __init
+lkkbd_init (void)
+{
+ return serio_register_driver(&lkkbd_drv);
+}
+
+static void __exit
+lkkbd_exit (void)
+{
+ serio_unregister_driver(&lkkbd_drv);
+}
+
+module_init (lkkbd_init);
+module_exit (lkkbd_exit);
+
diff --git a/drivers/input/keyboard/locomokbd.c b/drivers/input/keyboard/locomokbd.c
new file mode 100644
index 0000000..9caed30
--- /dev/null
+++ b/drivers/input/keyboard/locomokbd.c
@@ -0,0 +1,332 @@
+/*
+ * LoCoMo keyboard driver for Linux-based ARM PDAs:
+ * - SHARP Zaurus Collie (SL-5500)
+ * - SHARP Zaurus Poodle (SL-5600)
+ *
+ * Copyright (c) 2005 John Lenz
+ * Based on from xtkbd.c
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+
+#include <asm/hardware/locomo.h>
+#include <asm/irq.h>
+
+MODULE_AUTHOR("John Lenz <lenz@cs.wisc.edu>");
+MODULE_DESCRIPTION("LoCoMo keyboard driver");
+MODULE_LICENSE("GPL");
+
+#define LOCOMOKBD_NUMKEYS 128
+
+#define KEY_ACTIVITY KEY_F16
+#define KEY_CONTACT KEY_F18
+#define KEY_CENTER KEY_F15
+
+static const unsigned char
+locomokbd_keycode[LOCOMOKBD_NUMKEYS] __devinitconst = {
+ 0, KEY_ESC, KEY_ACTIVITY, 0, 0, 0, 0, 0, 0, 0, /* 0 - 9 */
+ 0, 0, 0, 0, 0, 0, 0, KEY_MENU, KEY_HOME, KEY_CONTACT, /* 10 - 19 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, /* 20 - 29 */
+ 0, 0, 0, KEY_CENTER, 0, KEY_MAIL, 0, 0, 0, 0, /* 30 - 39 */
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, KEY_RIGHT, /* 40 - 49 */
+ KEY_UP, KEY_LEFT, 0, 0, KEY_P, 0, KEY_O, KEY_I, KEY_Y, KEY_T, /* 50 - 59 */
+ KEY_E, KEY_W, 0, 0, 0, 0, KEY_DOWN, KEY_ENTER, 0, 0, /* 60 - 69 */
+ KEY_BACKSPACE, 0, KEY_L, KEY_U, KEY_H, KEY_R, KEY_D, KEY_Q, 0, 0, /* 70 - 79 */
+ 0, 0, 0, 0, 0, 0, KEY_ENTER, KEY_RIGHTSHIFT, KEY_K, KEY_J, /* 80 - 89 */
+ KEY_G, KEY_F, KEY_X, KEY_S, 0, 0, 0, 0, 0, 0, /* 90 - 99 */
+ 0, 0, KEY_DOT, 0, KEY_COMMA, KEY_N, KEY_B, KEY_C, KEY_Z, KEY_A, /* 100 - 109 */
+ KEY_LEFTSHIFT, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0, 0, 0, /* 110 - 119 */
+ KEY_M, KEY_SPACE, KEY_V, KEY_APOSTROPHE, KEY_SLASH, 0, 0, 0 /* 120 - 128 */
+};
+
+#define KB_ROWS 16
+#define KB_COLS 8
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(c,r) ( ((c)<<4) + (r) + 1 )
+
+#define KB_DELAY 8
+#define SCAN_INTERVAL (HZ/10)
+
+struct locomokbd {
+ unsigned char keycode[LOCOMOKBD_NUMKEYS];
+ struct input_dev *input;
+ char phys[32];
+
+ unsigned long base;
+ spinlock_t lock;
+
+ struct timer_list timer;
+ unsigned long suspend_jiffies;
+ unsigned int count_cancel;
+};
+
+/* helper functions for reading the keyboard matrix */
+static inline void locomokbd_charge_all(unsigned long membase)
+{
+ locomo_writel(0x00FF, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_activate_all(unsigned long membase)
+{
+ unsigned long r;
+
+ locomo_writel(0, membase + LOCOMO_KSC);
+ r = locomo_readl(membase + LOCOMO_KIC);
+ r &= 0xFEFF;
+ locomo_writel(r, membase + LOCOMO_KIC);
+}
+
+static inline void locomokbd_activate_col(unsigned long membase, int col)
+{
+ unsigned short nset;
+ unsigned short nbset;
+
+ nset = 0xFF & ~(1 << col);
+ nbset = (nset << 8) + nset;
+ locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+static inline void locomokbd_reset_col(unsigned long membase, int col)
+{
+ unsigned short nbset;
+
+ nbset = ((0xFF & ~(1 << col)) << 8) + 0xFF;
+ locomo_writel(nbset, membase + LOCOMO_KSC);
+}
+
+/*
+ * The LoCoMo keyboard only generates interrupts when a key is pressed.
+ * So when a key is pressed, we enable a timer. This timer scans the
+ * keyboard, and this is how we detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void locomokbd_scankeyboard(struct locomokbd *locomokbd)
+{
+ unsigned int row, col, rowd;
+ unsigned long flags;
+ unsigned int num_pressed;
+ unsigned long membase = locomokbd->base;
+
+ spin_lock_irqsave(&locomokbd->lock, flags);
+
+ locomokbd_charge_all(membase);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+
+ locomokbd_activate_col(membase, col);
+ udelay(KB_DELAY);
+
+ rowd = ~locomo_readl(membase + LOCOMO_KIB);
+ for (row = 0; row < KB_ROWS; row++) {
+ unsigned int scancode, pressed, key;
+
+ scancode = SCANCODE(col, row);
+ pressed = rowd & KB_ROWMASK(row);
+ key = locomokbd->keycode[scancode];
+
+ input_report_key(locomokbd->input, key, pressed);
+ if (likely(!pressed))
+ continue;
+
+ num_pressed++;
+
+ /* The "Cancel/ESC" key is labeled "On/Off" on
+ * Collie and Poodle and should suspend the device
+ * if it was pressed for more than a second. */
+ if (unlikely(key == KEY_ESC)) {
+ if (!time_after(jiffies,
+ locomokbd->suspend_jiffies + HZ))
+ continue;
+ if (locomokbd->count_cancel++
+ != (HZ/SCAN_INTERVAL + 1))
+ continue;
+ input_event(locomokbd->input, EV_PWR,
+ KEY_SUSPEND, 1);
+ locomokbd->suspend_jiffies = jiffies;
+ } else
+ locomokbd->count_cancel = 0;
+ }
+ locomokbd_reset_col(membase, col);
+ }
+ locomokbd_activate_all(membase);
+
+ input_sync(locomokbd->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&locomokbd->timer, jiffies + SCAN_INTERVAL);
+ else
+ locomokbd->count_cancel = 0;
+
+ spin_unlock_irqrestore(&locomokbd->lock, flags);
+}
+
+/*
+ * LoCoMo keyboard interrupt handler.
+ */
+static irqreturn_t locomokbd_interrupt(int irq, void *dev_id)
+{
+ struct locomokbd *locomokbd = dev_id;
+ /** wait chattering delay **/
+ udelay(100);
+
+ locomokbd_scankeyboard(locomokbd);
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * LoCoMo timer checking for released keys
+ */
+static void locomokbd_timer_callback(unsigned long data)
+{
+ struct locomokbd *locomokbd = (struct locomokbd *) data;
+
+ locomokbd_scankeyboard(locomokbd);
+}
+
+static int __devinit locomokbd_probe(struct locomo_dev *dev)
+{
+ struct locomokbd *locomokbd;
+ struct input_dev *input_dev;
+ int i, err;
+
+ locomokbd = kzalloc(sizeof(struct locomokbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!locomokbd || !input_dev) {
+ err = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ /* try and claim memory region */
+ if (!request_mem_region((unsigned long) dev->mapbase,
+ dev->length,
+ LOCOMO_DRIVER_NAME(dev))) {
+ err = -EBUSY;
+ printk(KERN_ERR "locomokbd: Can't acquire access to io memory for keyboard\n");
+ goto err_free_mem;
+ }
+
+ locomo_set_drvdata(dev, locomokbd);
+
+ locomokbd->base = (unsigned long) dev->mapbase;
+
+ spin_lock_init(&locomokbd->lock);
+
+ init_timer(&locomokbd->timer);
+ locomokbd->timer.function = locomokbd_timer_callback;
+ locomokbd->timer.data = (unsigned long) locomokbd;
+
+ locomokbd->suspend_jiffies = jiffies;
+
+ locomokbd->input = input_dev;
+ strcpy(locomokbd->phys, "locomokbd/input0");
+
+ input_dev->name = "LoCoMo keyboard";
+ input_dev->phys = locomokbd->phys;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+ BIT_MASK(EV_PWR);
+ input_dev->keycode = locomokbd->keycode;
+ input_dev->keycodesize = sizeof(locomokbd_keycode[0]);
+ input_dev->keycodemax = ARRAY_SIZE(locomokbd_keycode);
+
+ memcpy(locomokbd->keycode, locomokbd_keycode, sizeof(locomokbd->keycode));
+ for (i = 0; i < LOCOMOKBD_NUMKEYS; i++)
+ set_bit(locomokbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ /* attempt to get the interrupt */
+ err = request_irq(dev->irq[0], locomokbd_interrupt, 0, "locomokbd", locomokbd);
+ if (err) {
+ printk(KERN_ERR "locomokbd: Can't get irq for keyboard\n");
+ goto err_release_region;
+ }
+
+ err = input_register_device(locomokbd->input);
+ if (err)
+ goto err_free_irq;
+
+ return 0;
+
+ err_free_irq:
+ free_irq(dev->irq[0], locomokbd);
+ err_release_region:
+ release_mem_region((unsigned long) dev->mapbase, dev->length);
+ locomo_set_drvdata(dev, NULL);
+ err_free_mem:
+ input_free_device(input_dev);
+ kfree(locomokbd);
+
+ return err;
+}
+
+static int __devexit locomokbd_remove(struct locomo_dev *dev)
+{
+ struct locomokbd *locomokbd = locomo_get_drvdata(dev);
+
+ free_irq(dev->irq[0], locomokbd);
+
+ del_timer_sync(&locomokbd->timer);
+
+ input_unregister_device(locomokbd->input);
+ locomo_set_drvdata(dev, NULL);
+
+ release_mem_region((unsigned long) dev->mapbase, dev->length);
+
+ kfree(locomokbd);
+
+ return 0;
+}
+
+static struct locomo_driver keyboard_driver = {
+ .drv = {
+ .name = "locomokbd"
+ },
+ .devid = LOCOMO_DEVID_KEYBOARD,
+ .probe = locomokbd_probe,
+ .remove = __devexit_p(locomokbd_remove),
+};
+
+static int __init locomokbd_init(void)
+{
+ return locomo_driver_register(&keyboard_driver);
+}
+
+static void __exit locomokbd_exit(void)
+{
+ locomo_driver_unregister(&keyboard_driver);
+}
+
+module_init(locomokbd_init);
+module_exit(locomokbd_exit);
diff --git a/drivers/input/keyboard/maple_keyb.c b/drivers/input/keyboard/maple_keyb.c
new file mode 100644
index 0000000..22f17a5
--- /dev/null
+++ b/drivers/input/keyboard/maple_keyb.c
@@ -0,0 +1,253 @@
+/*
+ * SEGA Dreamcast keyboard driver
+ * Based on drivers/usb/usbkbd.c
+ * Copyright YAEGASHI Takeshi, 2001
+ * Porting to 2.6 Copyright Adrian McMenamin, 2007, 2008
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see the file COPYING, or write
+ * to the Free Software Foundation, Inc.,
+ * 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+ */
+
+#include <linux/kernel.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/timer.h>
+#include <linux/maple.h>
+
+/* Very simple mutex to ensure proper cleanup */
+static DEFINE_MUTEX(maple_keyb_mutex);
+
+#define NR_SCANCODES 256
+
+MODULE_AUTHOR("YAEGASHI Takeshi, Adrian McMenamin");
+MODULE_DESCRIPTION("SEGA Dreamcast keyboard driver");
+MODULE_LICENSE("GPL");
+
+struct dc_kbd {
+ struct input_dev *dev;
+ unsigned short keycode[NR_SCANCODES];
+ unsigned char new[8];
+ unsigned char old[8];
+};
+
+static const unsigned short dc_kbd_keycode[NR_SCANCODES] = {
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_A, KEY_B,
+ KEY_C, KEY_D, KEY_E, KEY_F, KEY_G, KEY_H, KEY_I, KEY_J, KEY_K, KEY_L,
+ KEY_M, KEY_N, KEY_O, KEY_P, KEY_Q, KEY_R, KEY_S, KEY_T, KEY_U, KEY_V,
+ KEY_W, KEY_X, KEY_Y, KEY_Z, KEY_1, KEY_2, KEY_3, KEY_4, KEY_5, KEY_6,
+ KEY_7, KEY_8, KEY_9, KEY_0, KEY_ENTER, KEY_ESC, KEY_BACKSPACE,
+ KEY_TAB, KEY_SPACE, KEY_MINUS, KEY_EQUAL, KEY_LEFTBRACE,
+ KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_BACKSLASH, KEY_SEMICOLON,
+ KEY_APOSTROPHE, KEY_GRAVE, KEY_COMMA, KEY_DOT, KEY_SLASH,
+ KEY_CAPSLOCK, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6,
+ KEY_F7, KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, KEY_SYSRQ,
+ KEY_SCROLLLOCK, KEY_PAUSE, KEY_INSERT, KEY_HOME, KEY_PAGEUP,
+ KEY_DELETE, KEY_END, KEY_PAGEDOWN, KEY_RIGHT, KEY_LEFT, KEY_DOWN,
+ KEY_UP, KEY_NUMLOCK, KEY_KPSLASH, KEY_KPASTERISK, KEY_KPMINUS,
+ KEY_KPPLUS, KEY_KPENTER, KEY_KP1, KEY_KP2, KEY_KP3, KEY_KP4, KEY_KP5,
+ KEY_KP6, KEY_KP7, KEY_KP8, KEY_KP9, KEY_KP0, KEY_KPDOT, KEY_102ND,
+ KEY_COMPOSE, KEY_POWER, KEY_KPEQUAL, KEY_F13, KEY_F14, KEY_F15,
+ KEY_F16, KEY_F17, KEY_F18, KEY_F19, KEY_F20, KEY_F21, KEY_F22,
+ KEY_F23, KEY_F24, KEY_OPEN, KEY_HELP, KEY_PROPS, KEY_FRONT, KEY_STOP,
+ KEY_AGAIN, KEY_UNDO, KEY_CUT, KEY_COPY, KEY_PASTE, KEY_FIND, KEY_MUTE,
+ KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_KPCOMMA, KEY_RESERVED, KEY_RO, KEY_KATAKANAHIRAGANA , KEY_YEN,
+ KEY_HENKAN, KEY_MUHENKAN, KEY_KPJPCOMMA, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_HANGEUL, KEY_HANJA, KEY_KATAKANA, KEY_HIRAGANA,
+ KEY_ZENKAKUHANKAKU, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED,
+ KEY_RESERVED, KEY_RESERVED, KEY_LEFTCTRL, KEY_LEFTSHIFT, KEY_LEFTALT,
+ KEY_LEFTMETA, KEY_RIGHTCTRL, KEY_RIGHTSHIFT, KEY_RIGHTALT,
+ KEY_RIGHTMETA, KEY_PLAYPAUSE, KEY_STOPCD, KEY_PREVIOUSSONG,
+ KEY_NEXTSONG, KEY_EJECTCD, KEY_VOLUMEUP, KEY_VOLUMEDOWN, KEY_MUTE,
+ KEY_WWW, KEY_BACK, KEY_FORWARD, KEY_STOP, KEY_FIND, KEY_SCROLLUP,
+ KEY_SCROLLDOWN, KEY_EDIT, KEY_SLEEP, KEY_SCREENLOCK, KEY_REFRESH,
+ KEY_CALC, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED, KEY_RESERVED
+};
+
+static void dc_scan_kbd(struct dc_kbd *kbd)
+{
+ struct input_dev *dev = kbd->dev;
+ void *ptr;
+ int code, keycode;
+ int i;
+
+ for (i = 0; i < 8; i++) {
+ code = i + 224;
+ keycode = kbd->keycode[code];
+ input_event(dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(dev, keycode, (kbd->new[0] >> i) & 1);
+ }
+
+ for (i = 2; i < 8; i++) {
+ ptr = memchr(kbd->new + 2, kbd->old[i], 6);
+ code = kbd->old[i];
+ if (code > 3 && ptr == NULL) {
+ keycode = kbd->keycode[code];
+ if (keycode) {
+ input_event(dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(dev, keycode, 0);
+ } else
+ printk(KERN_DEBUG "maple_keyb: "
+ "Unknown key (scancode %#x) released.",
+ code);
+ }
+ ptr = memchr(kbd->old + 2, kbd->new[i], 6);
+ code = kbd->new[i];
+ if (code > 3 && ptr) {
+ keycode = kbd->keycode[code];
+ if (keycode) {
+ input_event(dev, EV_MSC, MSC_SCAN, code);
+ input_report_key(dev, keycode, 1);
+ } else
+ printk(KERN_DEBUG "maple_keyb: "
+ "Unknown key (scancode %#x) pressed.",
+ code);
+ }
+ }
+ input_sync(dev);
+ memcpy(kbd->old, kbd->new, 8);
+}
+
+static void dc_kbd_callback(struct mapleq *mq)
+{
+ struct maple_device *mapledev = mq->dev;
+ struct dc_kbd *kbd = maple_get_drvdata(mapledev);
+ unsigned long *buf = mq->recvbuf;
+
+ /*
+ * We should always get the lock because the only
+ * time it may be locked is if the driver is in the cleanup phase.
+ */
+ if (likely(mutex_trylock(&maple_keyb_mutex))) {
+
+ if (buf[1] == mapledev->function) {
+ memcpy(kbd->new, buf + 2, 8);
+ dc_scan_kbd(kbd);
+ }
+
+ mutex_unlock(&maple_keyb_mutex);
+ }
+}
+
+static int probe_maple_kbd(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct maple_driver *mdrv = to_maple_driver(dev->driver);
+ int i, error;
+ struct dc_kbd *kbd;
+ struct input_dev *idev;
+
+ if (!(mdev->function & MAPLE_FUNC_KEYBOARD))
+ return -EINVAL;
+
+ kbd = kzalloc(sizeof(struct dc_kbd), GFP_KERNEL);
+ idev = input_allocate_device();
+ if (!kbd || !idev) {
+ error = -ENOMEM;
+ goto fail;
+ }
+
+ kbd->dev = idev;
+ memcpy(kbd->keycode, dc_kbd_keycode, sizeof(kbd->keycode));
+
+ idev->name = mdev->product_name;
+ idev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ idev->keycode = kbd->keycode;
+ idev->keycodesize = sizeof(unsigned short);
+ idev->keycodemax = ARRAY_SIZE(kbd->keycode);
+ idev->id.bustype = BUS_HOST;
+ idev->dev.parent = &mdev->dev;
+
+ for (i = 0; i < NR_SCANCODES; i++)
+ __set_bit(dc_kbd_keycode[i], idev->keybit);
+ __clear_bit(KEY_RESERVED, idev->keybit);
+
+ input_set_capability(idev, EV_MSC, MSC_SCAN);
+ input_set_drvdata(idev, kbd);
+
+ error = input_register_device(idev);
+ if (error)
+ goto fail;
+
+ /* Maple polling is locked to VBLANK - which may be just 50/s */
+ maple_getcond_callback(mdev, dc_kbd_callback, HZ/50,
+ MAPLE_FUNC_KEYBOARD);
+
+ mdev->driver = mdrv;
+
+ maple_set_drvdata(mdev, kbd);
+
+ return error;
+
+fail:
+ input_free_device(idev);
+ kfree(kbd);
+ maple_set_drvdata(mdev, NULL);
+ return error;
+}
+
+static int remove_maple_kbd(struct device *dev)
+{
+ struct maple_device *mdev = to_maple_dev(dev);
+ struct dc_kbd *kbd = maple_get_drvdata(mdev);
+
+ mutex_lock(&maple_keyb_mutex);
+
+ input_unregister_device(kbd->dev);
+ kfree(kbd);
+
+ maple_set_drvdata(mdev, NULL);
+
+ mutex_unlock(&maple_keyb_mutex);
+ return 0;
+}
+
+static struct maple_driver dc_kbd_driver = {
+ .function = MAPLE_FUNC_KEYBOARD,
+ .drv = {
+ .name = "Dreamcast_keyboard",
+ .probe = probe_maple_kbd,
+ .remove = remove_maple_kbd,
+ },
+};
+
+static int __init dc_kbd_init(void)
+{
+ return maple_driver_register(&dc_kbd_driver);
+}
+
+static void __exit dc_kbd_exit(void)
+{
+ maple_driver_unregister(&dc_kbd_driver);
+}
+
+module_init(dc_kbd_init);
+module_exit(dc_kbd_exit);
diff --git a/drivers/input/keyboard/newtonkbd.c b/drivers/input/keyboard/newtonkbd.c
new file mode 100644
index 0000000..48d1cab
--- /dev/null
+++ b/drivers/input/keyboard/newtonkbd.c
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2000 Justin Cormack
+ */
+
+/*
+ * Newton keyboard driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <j.cormack@doc.ic.ac.uk>, or by paper mail:
+ * Justin Cormack, 68 Dartmouth Park Road, London NW5 1SN, UK.
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Newton keyboard driver"
+
+MODULE_AUTHOR("Justin Cormack <j.cormack@doc.ic.ac.uk>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define NKBD_KEY 0x7f
+#define NKBD_PRESS 0x80
+
+static unsigned char nkbd_keycode[128] = {
+ KEY_A, KEY_S, KEY_D, KEY_F, KEY_H, KEY_G, KEY_Z, KEY_X,
+ KEY_C, KEY_V, 0, KEY_B, KEY_Q, KEY_W, KEY_E, KEY_R,
+ KEY_Y, KEY_T, KEY_1, KEY_2, KEY_3, KEY_4, KEY_6, KEY_5,
+ KEY_EQUAL, KEY_9, KEY_7, KEY_MINUS, KEY_8, KEY_0, KEY_RIGHTBRACE, KEY_O,
+ KEY_U, KEY_LEFTBRACE, KEY_I, KEY_P, KEY_ENTER, KEY_L, KEY_J, KEY_APOSTROPHE,
+ KEY_K, KEY_SEMICOLON, KEY_BACKSLASH, KEY_COMMA, KEY_SLASH, KEY_N, KEY_M, KEY_DOT,
+ KEY_TAB, KEY_SPACE, KEY_GRAVE, KEY_DELETE, 0, 0, 0, KEY_LEFTMETA,
+ KEY_LEFTSHIFT, KEY_CAPSLOCK, KEY_LEFTALT, KEY_LEFTCTRL, KEY_RIGHTSHIFT, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
+ KEY_LEFT, KEY_RIGHT, KEY_DOWN, KEY_UP, 0
+};
+
+struct nkbd {
+ unsigned char keycode[128];
+ struct input_dev *dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t nkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct nkbd *nkbd = serio_get_drvdata(serio);
+
+ /* invalid scan codes are probably the init sequence, so we ignore them */
+ if (nkbd->keycode[data & NKBD_KEY]) {
+ input_report_key(nkbd->dev, nkbd->keycode[data & NKBD_KEY], data & NKBD_PRESS);
+ input_sync(nkbd->dev);
+ }
+
+ else if (data == 0xe7) /* end of init sequence */
+ printk(KERN_INFO "input: %s on %s\n", nkbd->dev->name, serio->phys);
+ return IRQ_HANDLED;
+
+}
+
+static int nkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct nkbd *nkbd;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ nkbd = kzalloc(sizeof(struct nkbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!nkbd || !input_dev)
+ goto fail1;
+
+ nkbd->serio = serio;
+ nkbd->dev = input_dev;
+ snprintf(nkbd->phys, sizeof(nkbd->phys), "%s/input0", serio->phys);
+ memcpy(nkbd->keycode, nkbd_keycode, sizeof(nkbd->keycode));
+
+ input_dev->name = "Newton Keyboard";
+ input_dev->phys = nkbd->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_NEWTON;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->keycode = nkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(nkbd_keycode);
+ for (i = 0; i < 128; i++)
+ set_bit(nkbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ serio_set_drvdata(serio, nkbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(nkbd->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(nkbd);
+ return err;
+}
+
+static void nkbd_disconnect(struct serio *serio)
+{
+ struct nkbd *nkbd = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(nkbd->dev);
+ kfree(nkbd);
+}
+
+static struct serio_device_id nkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_NEWTON,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, nkbd_serio_ids);
+
+static struct serio_driver nkbd_drv = {
+ .driver = {
+ .name = "newtonkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = nkbd_serio_ids,
+ .interrupt = nkbd_interrupt,
+ .connect = nkbd_connect,
+ .disconnect = nkbd_disconnect,
+};
+
+static int __init nkbd_init(void)
+{
+ return serio_register_driver(&nkbd_drv);
+}
+
+static void __exit nkbd_exit(void)
+{
+ serio_unregister_driver(&nkbd_drv);
+}
+
+module_init(nkbd_init);
+module_exit(nkbd_exit);
diff --git a/drivers/input/keyboard/omap-keypad.c b/drivers/input/keyboard/omap-keypad.c
new file mode 100644
index 0000000..69e674e
--- /dev/null
+++ b/drivers/input/keyboard/omap-keypad.c
@@ -0,0 +1,487 @@
+/*
+ * linux/drivers/input/keyboard/omap-keypad.c
+ *
+ * OMAP Keypad Driver
+ *
+ * Copyright (C) 2003 Nokia Corporation
+ * Written by Timo Teräs <ext-timo.teras@nokia.com>
+ *
+ * Added support for H2 & H3 Keypad
+ * Copyright (C) 2004 Texas Instruments
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/types.h>
+#include <linux/input.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/errno.h>
+#include <mach/gpio.h>
+#include <mach/keypad.h>
+#include <mach/menelaus.h>
+#include <asm/irq.h>
+#include <mach/hardware.h>
+#include <asm/io.h>
+#include <mach/mux.h>
+
+#undef NEW_BOARD_LEARNING_MODE
+
+static void omap_kp_tasklet(unsigned long);
+static void omap_kp_timer(unsigned long);
+
+static unsigned char keypad_state[8];
+static DEFINE_MUTEX(kp_enable_mutex);
+static int kp_enable = 1;
+static int kp_cur_group = -1;
+
+struct omap_kp {
+ struct input_dev *input;
+ struct timer_list timer;
+ int irq;
+ unsigned int rows;
+ unsigned int cols;
+ unsigned long delay;
+ unsigned int debounce;
+};
+
+static DECLARE_TASKLET_DISABLED(kp_tasklet, omap_kp_tasklet, 0);
+
+static int *keymap;
+static unsigned int *row_gpios;
+static unsigned int *col_gpios;
+
+#ifdef CONFIG_ARCH_OMAP2
+static void set_col_gpio_val(struct omap_kp *omap_kp, u8 value)
+{
+ int col;
+
+ for (col = 0; col < omap_kp->cols; col++)
+ gpio_set_value(col_gpios[col], value & (1 << col));
+}
+
+static u8 get_row_gpio_val(struct omap_kp *omap_kp)
+{
+ int row;
+ u8 value = 0;
+
+ for (row = 0; row < omap_kp->rows; row++) {
+ if (gpio_get_value(row_gpios[row]))
+ value |= (1 << row);
+ }
+ return value;
+}
+#else
+#define set_col_gpio_val(x, y) do {} while (0)
+#define get_row_gpio_val(x) 0
+#endif
+
+static irqreturn_t omap_kp_interrupt(int irq, void *dev_id)
+{
+ struct omap_kp *omap_kp = dev_id;
+
+ /* disable keyboard interrupt and schedule for handling */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ tasklet_schedule(&kp_tasklet);
+
+ return IRQ_HANDLED;
+}
+
+static void omap_kp_timer(unsigned long data)
+{
+ tasklet_schedule(&kp_tasklet);
+}
+
+static void omap_kp_scan_keypad(struct omap_kp *omap_kp, unsigned char *state)
+{
+ int col = 0;
+
+ /* read the keypad status */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->rows; i++)
+ disable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+
+ /* read the keypad status */
+ for (col = 0; col < omap_kp->cols; col++) {
+ set_col_gpio_val(omap_kp, ~(1 << col));
+ state[col] = ~(get_row_gpio_val(omap_kp)) & 0x3f;
+ }
+ set_col_gpio_val(omap_kp, 0);
+
+ } else {
+ /* disable keyboard interrupt and schedule for handling */
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ /* read the keypad status */
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ for (col = 0; col < omap_kp->cols; col++) {
+ omap_writew(~(1 << col) & 0xff,
+ OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+
+ udelay(omap_kp->delay);
+
+ state[col] = ~omap_readw(OMAP_MPUIO_BASE +
+ OMAP_MPUIO_KBR_LATCH) & 0xff;
+ }
+ omap_writew(0x00, OMAP_MPUIO_BASE + OMAP_MPUIO_KBC);
+ udelay(2);
+ }
+}
+
+static inline int omap_kp_find_key(int col, int row)
+{
+ int i, key;
+
+ key = KEY(col, row, 0);
+ for (i = 0; keymap[i] != 0; i++)
+ if ((keymap[i] & 0xff000000) == key)
+ return keymap[i] & 0x00ffffff;
+ return -1;
+}
+
+static void omap_kp_tasklet(unsigned long data)
+{
+ struct omap_kp *omap_kp_data = (struct omap_kp *) data;
+ unsigned char new_state[8], changed, key_down = 0;
+ int col, row;
+ int spurious = 0;
+
+ /* check for any changes */
+ omap_kp_scan_keypad(omap_kp_data, new_state);
+
+ /* check for changes and print those */
+ for (col = 0; col < omap_kp_data->cols; col++) {
+ changed = new_state[col] ^ keypad_state[col];
+ key_down |= new_state[col];
+ if (changed == 0)
+ continue;
+
+ for (row = 0; row < omap_kp_data->rows; row++) {
+ int key;
+ if (!(changed & (1 << row)))
+ continue;
+#ifdef NEW_BOARD_LEARNING_MODE
+ printk(KERN_INFO "omap-keypad: key %d-%d %s\n", col,
+ row, (new_state[col] & (1 << row)) ?
+ "pressed" : "released");
+#else
+ key = omap_kp_find_key(col, row);
+ if (key < 0) {
+ printk(KERN_WARNING
+ "omap-keypad: Spurious key event %d-%d\n",
+ col, row);
+ /* We scan again after a couple of seconds */
+ spurious = 1;
+ continue;
+ }
+
+ if (!(kp_cur_group == (key & GROUP_MASK) ||
+ kp_cur_group == -1))
+ continue;
+
+ kp_cur_group = key & GROUP_MASK;
+ input_report_key(omap_kp_data->input, key & ~GROUP_MASK,
+ new_state[col] & (1 << row));
+#endif
+ }
+ }
+ memcpy(keypad_state, new_state, sizeof(keypad_state));
+
+ if (key_down) {
+ int delay = HZ / 20;
+ /* some key is pressed - keep irq disabled and use timer
+ * to poll the keypad */
+ if (spurious)
+ delay = 2 * HZ;
+ mod_timer(&omap_kp_data->timer, jiffies + delay);
+ } else {
+ /* enable interrupts */
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp_data->rows; i++)
+ enable_irq(OMAP_GPIO_IRQ(row_gpios[i]));
+ } else {
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ kp_cur_group = -1;
+ }
+ }
+}
+
+static ssize_t omap_kp_enable_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ return sprintf(buf, "%u\n", kp_enable);
+}
+
+static ssize_t omap_kp_enable_store(struct device *dev, struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int state;
+
+ if (sscanf(buf, "%u", &state) != 1)
+ return -EINVAL;
+
+ if ((state != 1) && (state != 0))
+ return -EINVAL;
+
+ mutex_lock(&kp_enable_mutex);
+ if (state != kp_enable) {
+ if (state)
+ enable_irq(INT_KEYBOARD);
+ else
+ disable_irq(INT_KEYBOARD);
+ kp_enable = state;
+ }
+ mutex_unlock(&kp_enable_mutex);
+
+ return strnlen(buf, count);
+}
+
+static DEVICE_ATTR(enable, S_IRUGO | S_IWUSR, omap_kp_enable_show, omap_kp_enable_store);
+
+#ifdef CONFIG_PM
+static int omap_kp_suspend(struct platform_device *dev, pm_message_t state)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+
+static int omap_kp_resume(struct platform_device *dev)
+{
+ /* Nothing yet */
+
+ return 0;
+}
+#else
+#define omap_kp_suspend NULL
+#define omap_kp_resume NULL
+#endif
+
+static int __init omap_kp_probe(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp;
+ struct input_dev *input_dev;
+ struct omap_kp_platform_data *pdata = pdev->dev.platform_data;
+ int i, col_idx, row_idx, irq_idx, ret;
+
+ if (!pdata->rows || !pdata->cols || !pdata->keymap) {
+ printk(KERN_ERR "No rows, cols or keymap from pdata\n");
+ return -EINVAL;
+ }
+
+ omap_kp = kzalloc(sizeof(struct omap_kp), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!omap_kp || !input_dev) {
+ kfree(omap_kp);
+ input_free_device(input_dev);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, omap_kp);
+
+ omap_kp->input = input_dev;
+
+ /* Disable the interrupt for the MPUIO keyboard */
+ if (!cpu_is_omap24xx())
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+
+ keymap = pdata->keymap;
+
+ if (pdata->rep)
+ __set_bit(EV_REP, input_dev->evbit);
+
+ if (pdata->delay)
+ omap_kp->delay = pdata->delay;
+
+ if (pdata->row_gpios && pdata->col_gpios) {
+ row_gpios = pdata->row_gpios;
+ col_gpios = pdata->col_gpios;
+ }
+
+ omap_kp->rows = pdata->rows;
+ omap_kp->cols = pdata->cols;
+
+ if (cpu_is_omap24xx()) {
+ /* Cols: outputs */
+ for (col_idx = 0; col_idx < omap_kp->cols; col_idx++) {
+ if (gpio_request(col_gpios[col_idx], "omap_kp_col") < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ col_gpios[col_idx]);
+ goto err1;
+ }
+ gpio_direction_output(col_gpios[col_idx], 0);
+ }
+ /* Rows: inputs */
+ for (row_idx = 0; row_idx < omap_kp->rows; row_idx++) {
+ if (gpio_request(row_gpios[row_idx], "omap_kp_row") < 0) {
+ printk(KERN_ERR "Failed to request"
+ "GPIO%d for keypad\n",
+ row_gpios[row_idx]);
+ goto err2;
+ }
+ gpio_direction_input(row_gpios[row_idx]);
+ }
+ } else {
+ col_idx = 0;
+ row_idx = 0;
+ }
+
+ setup_timer(&omap_kp->timer, omap_kp_timer, (unsigned long)omap_kp);
+
+ /* get the irq and init timer*/
+ tasklet_enable(&kp_tasklet);
+ kp_tasklet.data = (unsigned long) omap_kp;
+
+ ret = device_create_file(&pdev->dev, &dev_attr_enable);
+ if (ret < 0)
+ goto err2;
+
+ /* setup input device */
+ __set_bit(EV_KEY, input_dev->evbit);
+ for (i = 0; keymap[i] != 0; i++)
+ __set_bit(keymap[i] & KEY_MAX, input_dev->keybit);
+ input_dev->name = "omap-keypad";
+ input_dev->phys = "omap-keypad/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ ret = input_register_device(omap_kp->input);
+ if (ret < 0) {
+ printk(KERN_ERR "Unable to register omap-keypad input device\n");
+ goto err3;
+ }
+
+ if (pdata->dbounce)
+ omap_writew(0xff, OMAP_MPUIO_BASE + OMAP_MPUIO_GPIO_DEBOUNCING);
+
+ /* scan current status and enable interrupt */
+ omap_kp_scan_keypad(omap_kp, keypad_state);
+ if (!cpu_is_omap24xx()) {
+ omap_kp->irq = platform_get_irq(pdev, 0);
+ if (omap_kp->irq >= 0) {
+ if (request_irq(omap_kp->irq, omap_kp_interrupt, 0,
+ "omap-keypad", omap_kp) < 0)
+ goto err4;
+ }
+ omap_writew(0, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ } else {
+ for (irq_idx = 0; irq_idx < omap_kp->rows; irq_idx++) {
+ if (request_irq(OMAP_GPIO_IRQ(row_gpios[irq_idx]),
+ omap_kp_interrupt,
+ IRQF_TRIGGER_FALLING,
+ "omap-keypad", omap_kp) < 0)
+ goto err5;
+ }
+ }
+ return 0;
+err5:
+ for (i = irq_idx - 1; i >=0; i--)
+ free_irq(row_gpios[i], 0);
+err4:
+ input_unregister_device(omap_kp->input);
+ input_dev = NULL;
+err3:
+ device_remove_file(&pdev->dev, &dev_attr_enable);
+err2:
+ for (i = row_idx - 1; i >=0; i--)
+ gpio_free(row_gpios[i]);
+err1:
+ for (i = col_idx - 1; i >=0; i--)
+ gpio_free(col_gpios[i]);
+
+ kfree(omap_kp);
+ input_free_device(input_dev);
+
+ return -EINVAL;
+}
+
+static int omap_kp_remove(struct platform_device *pdev)
+{
+ struct omap_kp *omap_kp = platform_get_drvdata(pdev);
+
+ /* disable keypad interrupt handling */
+ tasklet_disable(&kp_tasklet);
+ if (cpu_is_omap24xx()) {
+ int i;
+ for (i = 0; i < omap_kp->cols; i++)
+ gpio_free(col_gpios[i]);
+ for (i = 0; i < omap_kp->rows; i++) {
+ gpio_free(row_gpios[i]);
+ free_irq(OMAP_GPIO_IRQ(row_gpios[i]), 0);
+ }
+ } else {
+ omap_writew(1, OMAP_MPUIO_BASE + OMAP_MPUIO_KBD_MASKIT);
+ free_irq(omap_kp->irq, 0);
+ }
+
+ del_timer_sync(&omap_kp->timer);
+ tasklet_kill(&kp_tasklet);
+
+ /* unregister everything */
+ input_unregister_device(omap_kp->input);
+
+ kfree(omap_kp);
+
+ return 0;
+}
+
+static struct platform_driver omap_kp_driver = {
+ .probe = omap_kp_probe,
+ .remove = omap_kp_remove,
+ .suspend = omap_kp_suspend,
+ .resume = omap_kp_resume,
+ .driver = {
+ .name = "omap-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit omap_kp_init(void)
+{
+ printk(KERN_INFO "OMAP Keypad Driver\n");
+ return platform_driver_register(&omap_kp_driver);
+}
+
+static void __exit omap_kp_exit(void)
+{
+ platform_driver_unregister(&omap_kp_driver);
+}
+
+module_init(omap_kp_init);
+module_exit(omap_kp_exit);
+
+MODULE_AUTHOR("Timo Teräs");
+MODULE_DESCRIPTION("OMAP Keypad Driver");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:omap-keypad");
diff --git a/drivers/input/keyboard/pxa27x_keypad.c b/drivers/input/keyboard/pxa27x_keypad.c
new file mode 100644
index 0000000..6d30c6d
--- /dev/null
+++ b/drivers/input/keyboard/pxa27x_keypad.c
@@ -0,0 +1,600 @@
+/*
+ * linux/drivers/input/keyboard/pxa27x_keypad.c
+ *
+ * Driver for the pxa27x matrix keyboard controller.
+ *
+ * Created: Feb 22, 2007
+ * Author: Rodolfo Giometti <giometti@linux.it>
+ *
+ * Based on a previous implementations by Kevin O'Connor
+ * <kevin_at_koconnor.net> and Alex Osborne <bobofdoom@gmail.com> and
+ * on some suggestions by Nicolas Pitre <nico@cam.org>.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+#include <asm/mach/arch.h>
+#include <asm/mach/map.h>
+
+#include <mach/hardware.h>
+#include <mach/pxa27x_keypad.h>
+
+/*
+ * Keypad Controller registers
+ */
+#define KPC 0x0000 /* Keypad Control register */
+#define KPDK 0x0008 /* Keypad Direct Key register */
+#define KPREC 0x0010 /* Keypad Rotary Encoder register */
+#define KPMK 0x0018 /* Keypad Matrix Key register */
+#define KPAS 0x0020 /* Keypad Automatic Scan register */
+
+/* Keypad Automatic Scan Multiple Key Presser register 0-3 */
+#define KPASMKP0 0x0028
+#define KPASMKP1 0x0030
+#define KPASMKP2 0x0038
+#define KPASMKP3 0x0040
+#define KPKDI 0x0048
+
+/* bit definitions */
+#define KPC_MKRN(n) ((((n) - 1) & 0x7) << 26) /* matrix key row number */
+#define KPC_MKCN(n) ((((n) - 1) & 0x7) << 23) /* matrix key column number */
+#define KPC_DKN(n) ((((n) - 1) & 0x7) << 6) /* direct key number */
+
+#define KPC_AS (0x1 << 30) /* Automatic Scan bit */
+#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */
+#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */
+#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */
+
+#define KPC_MS(n) (0x1 << (13 + (n))) /* Matrix scan line 'n' */
+#define KPC_MS_ALL (0xff << 13)
+
+#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */
+#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */
+#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Keypad Debounce Select */
+#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */
+#define KPC_RE_ZERO_DEB (0x1 << 4) /* Rotary Encoder Zero Debounce */
+#define KPC_REE1 (0x1 << 3) /* Rotary Encoder1 Enable */
+#define KPC_REE0 (0x1 << 2) /* Rotary Encoder0 Enable */
+#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */
+#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */
+
+#define KPDK_DKP (0x1 << 31)
+#define KPDK_DK(n) ((n) & 0xff)
+
+#define KPREC_OF1 (0x1 << 31)
+#define kPREC_UF1 (0x1 << 30)
+#define KPREC_OF0 (0x1 << 15)
+#define KPREC_UF0 (0x1 << 14)
+
+#define KPREC_RECOUNT0(n) ((n) & 0xff)
+#define KPREC_RECOUNT1(n) (((n) >> 16) & 0xff)
+
+#define KPMK_MKP (0x1 << 31)
+#define KPAS_SO (0x1 << 31)
+#define KPASMKPx_SO (0x1 << 31)
+
+#define KPAS_MUKP(n) (((n) >> 26) & 0x1f)
+#define KPAS_RP(n) (((n) >> 4) & 0xf)
+#define KPAS_CP(n) ((n) & 0xf)
+
+#define KPASMKP_MKC_MASK (0xff)
+
+#define keypad_readl(off) __raw_readl(keypad->mmio_base + (off))
+#define keypad_writel(off, v) __raw_writel((v), keypad->mmio_base + (off))
+
+#define MAX_MATRIX_KEY_NUM (8 * 8)
+
+struct pxa27x_keypad {
+ struct pxa27x_keypad_platform_data *pdata;
+
+ struct clk *clk;
+ struct input_dev *input_dev;
+ void __iomem *mmio_base;
+
+ int irq;
+
+ /* matrix key code map */
+ unsigned int matrix_keycodes[MAX_MATRIX_KEY_NUM];
+
+ /* state row bits of each column scan */
+ uint32_t matrix_key_state[MAX_MATRIX_KEY_COLS];
+ uint32_t direct_key_state;
+
+ unsigned int direct_key_mask;
+
+ int rotary_rel_code[2];
+ int rotary_up_key[2];
+ int rotary_down_key[2];
+};
+
+static void pxa27x_keypad_build_keycode(struct pxa27x_keypad *keypad)
+{
+ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ struct input_dev *input_dev = keypad->input_dev;
+ unsigned int *key;
+ int i;
+
+ key = &pdata->matrix_key_map[0];
+ for (i = 0; i < pdata->matrix_key_map_size; i++, key++) {
+ int row = ((*key) >> 28) & 0xf;
+ int col = ((*key) >> 24) & 0xf;
+ int code = (*key) & 0xffffff;
+
+ keypad->matrix_keycodes[(row << 3) + col] = code;
+ set_bit(code, input_dev->keybit);
+ }
+
+ for (i = 0; i < pdata->direct_key_num; i++)
+ set_bit(pdata->direct_key_map[i], input_dev->keybit);
+
+ keypad->rotary_up_key[0] = pdata->rotary0_up_key;
+ keypad->rotary_up_key[1] = pdata->rotary1_up_key;
+ keypad->rotary_down_key[0] = pdata->rotary0_down_key;
+ keypad->rotary_down_key[1] = pdata->rotary1_down_key;
+ keypad->rotary_rel_code[0] = pdata->rotary0_rel_code;
+ keypad->rotary_rel_code[1] = pdata->rotary1_rel_code;
+
+ if (pdata->enable_rotary0) {
+ if (pdata->rotary0_up_key && pdata->rotary0_down_key) {
+ set_bit(pdata->rotary0_up_key, input_dev->keybit);
+ set_bit(pdata->rotary0_down_key, input_dev->keybit);
+ } else
+ set_bit(pdata->rotary0_rel_code, input_dev->relbit);
+ }
+
+ if (pdata->enable_rotary1) {
+ if (pdata->rotary1_up_key && pdata->rotary1_down_key) {
+ set_bit(pdata->rotary1_up_key, input_dev->keybit);
+ set_bit(pdata->rotary1_down_key, input_dev->keybit);
+ } else
+ set_bit(pdata->rotary1_rel_code, input_dev->relbit);
+ }
+}
+
+static inline unsigned int lookup_matrix_keycode(
+ struct pxa27x_keypad *keypad, int row, int col)
+{
+ return keypad->matrix_keycodes[(row << 3) + col];
+}
+
+static void pxa27x_keypad_scan_matrix(struct pxa27x_keypad *keypad)
+{
+ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ int row, col, num_keys_pressed = 0;
+ uint32_t new_state[MAX_MATRIX_KEY_COLS];
+ uint32_t kpas = keypad_readl(KPAS);
+
+ num_keys_pressed = KPAS_MUKP(kpas);
+
+ memset(new_state, 0, sizeof(new_state));
+
+ if (num_keys_pressed == 0)
+ goto scan;
+
+ if (num_keys_pressed == 1) {
+ col = KPAS_CP(kpas);
+ row = KPAS_RP(kpas);
+
+ /* if invalid row/col, treat as no key pressed */
+ if (col >= pdata->matrix_key_cols ||
+ row >= pdata->matrix_key_rows)
+ goto scan;
+
+ new_state[col] = (1 << row);
+ goto scan;
+ }
+
+ if (num_keys_pressed > 1) {
+ uint32_t kpasmkp0 = keypad_readl(KPASMKP0);
+ uint32_t kpasmkp1 = keypad_readl(KPASMKP1);
+ uint32_t kpasmkp2 = keypad_readl(KPASMKP2);
+ uint32_t kpasmkp3 = keypad_readl(KPASMKP3);
+
+ new_state[0] = kpasmkp0 & KPASMKP_MKC_MASK;
+ new_state[1] = (kpasmkp0 >> 16) & KPASMKP_MKC_MASK;
+ new_state[2] = kpasmkp1 & KPASMKP_MKC_MASK;
+ new_state[3] = (kpasmkp1 >> 16) & KPASMKP_MKC_MASK;
+ new_state[4] = kpasmkp2 & KPASMKP_MKC_MASK;
+ new_state[5] = (kpasmkp2 >> 16) & KPASMKP_MKC_MASK;
+ new_state[6] = kpasmkp3 & KPASMKP_MKC_MASK;
+ new_state[7] = (kpasmkp3 >> 16) & KPASMKP_MKC_MASK;
+ }
+scan:
+ for (col = 0; col < pdata->matrix_key_cols; col++) {
+ uint32_t bits_changed;
+
+ bits_changed = keypad->matrix_key_state[col] ^ new_state[col];
+ if (bits_changed == 0)
+ continue;
+
+ for (row = 0; row < pdata->matrix_key_rows; row++) {
+ if ((bits_changed & (1 << row)) == 0)
+ continue;
+
+ input_report_key(keypad->input_dev,
+ lookup_matrix_keycode(keypad, row, col),
+ new_state[col] & (1 << row));
+ }
+ }
+ input_sync(keypad->input_dev);
+ memcpy(keypad->matrix_key_state, new_state, sizeof(new_state));
+}
+
+#define DEFAULT_KPREC (0x007f007f)
+
+static inline int rotary_delta(uint32_t kprec)
+{
+ if (kprec & KPREC_OF0)
+ return (kprec & 0xff) + 0x7f;
+ else if (kprec & KPREC_UF0)
+ return (kprec & 0xff) - 0x7f - 0xff;
+ else
+ return (kprec & 0xff) - 0x7f;
+}
+
+static void report_rotary_event(struct pxa27x_keypad *keypad, int r, int delta)
+{
+ struct input_dev *dev = keypad->input_dev;
+
+ if (delta == 0)
+ return;
+
+ if (keypad->rotary_up_key[r] && keypad->rotary_down_key[r]) {
+ int keycode = (delta > 0) ? keypad->rotary_up_key[r] :
+ keypad->rotary_down_key[r];
+
+ /* simulate a press-n-release */
+ input_report_key(dev, keycode, 1);
+ input_sync(dev);
+ input_report_key(dev, keycode, 0);
+ input_sync(dev);
+ } else {
+ input_report_rel(dev, keypad->rotary_rel_code[r], delta);
+ input_sync(dev);
+ }
+}
+
+static void pxa27x_keypad_scan_rotary(struct pxa27x_keypad *keypad)
+{
+ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ uint32_t kprec;
+
+ /* read and reset to default count value */
+ kprec = keypad_readl(KPREC);
+ keypad_writel(KPREC, DEFAULT_KPREC);
+
+ if (pdata->enable_rotary0)
+ report_rotary_event(keypad, 0, rotary_delta(kprec));
+
+ if (pdata->enable_rotary1)
+ report_rotary_event(keypad, 1, rotary_delta(kprec >> 16));
+}
+
+static void pxa27x_keypad_scan_direct(struct pxa27x_keypad *keypad)
+{
+ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ unsigned int new_state;
+ uint32_t kpdk, bits_changed;
+ int i;
+
+ kpdk = keypad_readl(KPDK);
+
+ if (pdata->enable_rotary0 || pdata->enable_rotary1)
+ pxa27x_keypad_scan_rotary(keypad);
+
+ if (pdata->direct_key_map == NULL)
+ return;
+
+ new_state = KPDK_DK(kpdk) & keypad->direct_key_mask;
+ bits_changed = keypad->direct_key_state ^ new_state;
+
+ if (bits_changed == 0)
+ return;
+
+ for (i = 0; i < pdata->direct_key_num; i++) {
+ if (bits_changed & (1 << i))
+ input_report_key(keypad->input_dev,
+ pdata->direct_key_map[i],
+ (new_state & (1 << i)));
+ }
+ input_sync(keypad->input_dev);
+ keypad->direct_key_state = new_state;
+}
+
+static irqreturn_t pxa27x_keypad_irq_handler(int irq, void *dev_id)
+{
+ struct pxa27x_keypad *keypad = dev_id;
+ unsigned long kpc = keypad_readl(KPC);
+
+ if (kpc & KPC_DI)
+ pxa27x_keypad_scan_direct(keypad);
+
+ if (kpc & KPC_MI)
+ pxa27x_keypad_scan_matrix(keypad);
+
+ return IRQ_HANDLED;
+}
+
+static void pxa27x_keypad_config(struct pxa27x_keypad *keypad)
+{
+ struct pxa27x_keypad_platform_data *pdata = keypad->pdata;
+ unsigned int mask = 0, direct_key_num = 0;
+ unsigned long kpc = 0;
+
+ /* enable matrix keys with automatic scan */
+ if (pdata->matrix_key_rows && pdata->matrix_key_cols) {
+ kpc |= KPC_ASACT | KPC_MIE | KPC_ME | KPC_MS_ALL;
+ kpc |= KPC_MKRN(pdata->matrix_key_rows) |
+ KPC_MKCN(pdata->matrix_key_cols);
+ }
+
+ /* enable rotary key, debounce interval same as direct keys */
+ if (pdata->enable_rotary0) {
+ mask |= 0x03;
+ direct_key_num = 2;
+ kpc |= KPC_REE0;
+ }
+
+ if (pdata->enable_rotary1) {
+ mask |= 0x0c;
+ direct_key_num = 4;
+ kpc |= KPC_REE1;
+ }
+
+ if (pdata->direct_key_num > direct_key_num)
+ direct_key_num = pdata->direct_key_num;
+
+ keypad->direct_key_mask = ((2 << direct_key_num) - 1) & ~mask;
+
+ /* enable direct key */
+ if (direct_key_num)
+ kpc |= KPC_DE | KPC_DIE | KPC_DKN(direct_key_num);
+
+ keypad_writel(KPC, kpc | KPC_RE_ZERO_DEB);
+ keypad_writel(KPREC, DEFAULT_KPREC);
+ keypad_writel(KPKDI, pdata->debounce_interval);
+}
+
+static int pxa27x_keypad_open(struct input_dev *dev)
+{
+ struct pxa27x_keypad *keypad = input_get_drvdata(dev);
+
+ /* Enable unit clock */
+ clk_enable(keypad->clk);
+ pxa27x_keypad_config(keypad);
+
+ return 0;
+}
+
+static void pxa27x_keypad_close(struct input_dev *dev)
+{
+ struct pxa27x_keypad *keypad = input_get_drvdata(dev);
+
+ /* Disable clock unit */
+ clk_disable(keypad->clk);
+}
+
+#ifdef CONFIG_PM
+static int pxa27x_keypad_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
+
+ clk_disable(keypad->clk);
+
+ if (device_may_wakeup(&pdev->dev))
+ enable_irq_wake(keypad->irq);
+
+ return 0;
+}
+
+static int pxa27x_keypad_resume(struct platform_device *pdev)
+{
+ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
+ struct input_dev *input_dev = keypad->input_dev;
+
+ if (device_may_wakeup(&pdev->dev))
+ disable_irq_wake(keypad->irq);
+
+ mutex_lock(&input_dev->mutex);
+
+ if (input_dev->users) {
+ /* Enable unit clock */
+ clk_enable(keypad->clk);
+ pxa27x_keypad_config(keypad);
+ }
+
+ mutex_unlock(&input_dev->mutex);
+
+ return 0;
+}
+#else
+#define pxa27x_keypad_suspend NULL
+#define pxa27x_keypad_resume NULL
+#endif
+
+#define res_size(res) ((res)->end - (res)->start + 1)
+
+static int __devinit pxa27x_keypad_probe(struct platform_device *pdev)
+{
+ struct pxa27x_keypad *keypad;
+ struct input_dev *input_dev;
+ struct resource *res;
+ int irq, error;
+
+ keypad = kzalloc(sizeof(struct pxa27x_keypad), GFP_KERNEL);
+ if (keypad == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ return -ENOMEM;
+ }
+
+ keypad->pdata = pdev->dev.platform_data;
+ if (keypad->pdata == NULL) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ error = -EINVAL;
+ goto failed_free;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get keypad irq\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ error = -ENXIO;
+ goto failed_free;
+ }
+
+ res = request_mem_region(res->start, res_size(res), pdev->name);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to request I/O memory\n");
+ error = -EBUSY;
+ goto failed_free;
+ }
+
+ keypad->mmio_base = ioremap(res->start, res_size(res));
+ if (keypad->mmio_base == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto failed_free_mem;
+ }
+
+ keypad->clk = clk_get(&pdev->dev, "KBDCLK");
+ if (IS_ERR(keypad->clk)) {
+ dev_err(&pdev->dev, "failed to get keypad clock\n");
+ error = PTR_ERR(keypad->clk);
+ goto failed_free_io;
+ }
+
+ /* Create and register the input driver. */
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ error = -ENOMEM;
+ goto failed_put_clk;
+ }
+
+ input_dev->name = pdev->name;
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->open = pxa27x_keypad_open;
+ input_dev->close = pxa27x_keypad_close;
+ input_dev->dev.parent = &pdev->dev;
+
+ keypad->input_dev = input_dev;
+ input_set_drvdata(input_dev, keypad);
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ if ((keypad->pdata->enable_rotary0 &&
+ keypad->pdata->rotary0_rel_code) ||
+ (keypad->pdata->enable_rotary1 &&
+ keypad->pdata->rotary1_rel_code)) {
+ input_dev->evbit[0] |= BIT_MASK(EV_REL);
+ }
+
+ pxa27x_keypad_build_keycode(keypad);
+ platform_set_drvdata(pdev, keypad);
+
+ error = request_irq(irq, pxa27x_keypad_irq_handler, IRQF_DISABLED,
+ pdev->name, keypad);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto failed_free_dev;
+ }
+
+ keypad->irq = irq;
+
+ /* Register the input device */
+ error = input_register_device(input_dev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto failed_free_irq;
+ }
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+failed_free_irq:
+ free_irq(irq, pdev);
+ platform_set_drvdata(pdev, NULL);
+failed_free_dev:
+ input_free_device(input_dev);
+failed_put_clk:
+ clk_put(keypad->clk);
+failed_free_io:
+ iounmap(keypad->mmio_base);
+failed_free_mem:
+ release_mem_region(res->start, res_size(res));
+failed_free:
+ kfree(keypad);
+ return error;
+}
+
+static int __devexit pxa27x_keypad_remove(struct platform_device *pdev)
+{
+ struct pxa27x_keypad *keypad = platform_get_drvdata(pdev);
+ struct resource *res;
+
+ free_irq(keypad->irq, pdev);
+
+ clk_disable(keypad->clk);
+ clk_put(keypad->clk);
+
+ input_unregister_device(keypad->input_dev);
+ input_free_device(keypad->input_dev);
+
+ iounmap(keypad->mmio_base);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ release_mem_region(res->start, res_size(res));
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(keypad);
+ return 0;
+}
+
+/* work with hotplug and coldplug */
+MODULE_ALIAS("platform:pxa27x-keypad");
+
+static struct platform_driver pxa27x_keypad_driver = {
+ .probe = pxa27x_keypad_probe,
+ .remove = __devexit_p(pxa27x_keypad_remove),
+ .suspend = pxa27x_keypad_suspend,
+ .resume = pxa27x_keypad_resume,
+ .driver = {
+ .name = "pxa27x-keypad",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __init pxa27x_keypad_init(void)
+{
+ return platform_driver_register(&pxa27x_keypad_driver);
+}
+
+static void __exit pxa27x_keypad_exit(void)
+{
+ platform_driver_unregister(&pxa27x_keypad_driver);
+}
+
+module_init(pxa27x_keypad_init);
+module_exit(pxa27x_keypad_exit);
+
+MODULE_DESCRIPTION("PXA27x Keypad Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/sh_keysc.c b/drivers/input/keyboard/sh_keysc.c
new file mode 100644
index 0000000..c600ab7
--- /dev/null
+++ b/drivers/input/keyboard/sh_keysc.c
@@ -0,0 +1,267 @@
+/*
+ * SuperH KEYSC Keypad Driver
+ *
+ * Copyright (C) 2008 Magnus Damm
+ *
+ * Based on gpio_keys.c, Copyright 2005 Phil Blundell
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <asm/sh_keysc.h>
+
+#define KYCR1_OFFS 0x00
+#define KYCR2_OFFS 0x04
+#define KYINDR_OFFS 0x08
+#define KYOUTDR_OFFS 0x0c
+
+#define KYCR2_IRQ_LEVEL 0x10
+#define KYCR2_IRQ_DISABLED 0x00
+
+static const struct {
+ unsigned char kymd, keyout, keyin;
+} sh_keysc_mode[] = {
+ [SH_KEYSC_MODE_1] = { 0, 6, 5 },
+ [SH_KEYSC_MODE_2] = { 1, 5, 6 },
+ [SH_KEYSC_MODE_3] = { 2, 4, 7 },
+};
+
+struct sh_keysc_priv {
+ void __iomem *iomem_base;
+ unsigned long last_keys;
+ struct input_dev *input;
+ struct sh_keysc_info pdata;
+};
+
+static irqreturn_t sh_keysc_isr(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+ struct sh_keysc_info *pdata = &priv->pdata;
+ unsigned long keys, keys1, keys0, mask;
+ unsigned char keyin_set, tmp;
+ int i, k;
+
+ dev_dbg(&pdev->dev, "isr!\n");
+
+ keys1 = ~0;
+ keys0 = 0;
+
+ do {
+ keys = 0;
+ keyin_set = 0;
+
+ iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
+
+ for (i = 0; i < sh_keysc_mode[pdata->mode].keyout; i++) {
+ iowrite16(0xfff ^ (3 << (i * 2)),
+ priv->iomem_base + KYOUTDR_OFFS);
+ udelay(pdata->delay);
+ tmp = ioread16(priv->iomem_base + KYINDR_OFFS);
+ keys |= tmp << (sh_keysc_mode[pdata->mode].keyin * i);
+ tmp ^= (1 << sh_keysc_mode[pdata->mode].keyin) - 1;
+ keyin_set |= tmp;
+ }
+
+ iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
+ iowrite16(KYCR2_IRQ_LEVEL | (keyin_set << 8),
+ priv->iomem_base + KYCR2_OFFS);
+
+ keys ^= ~0;
+ keys &= (1 << (sh_keysc_mode[pdata->mode].keyin *
+ sh_keysc_mode[pdata->mode].keyout)) - 1;
+ keys1 &= keys;
+ keys0 |= keys;
+
+ dev_dbg(&pdev->dev, "keys 0x%08lx\n", keys);
+
+ } while (ioread16(priv->iomem_base + KYCR2_OFFS) & 0x01);
+
+ dev_dbg(&pdev->dev, "last_keys 0x%08lx keys0 0x%08lx keys1 0x%08lx\n",
+ priv->last_keys, keys0, keys1);
+
+ for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
+ k = pdata->keycodes[i];
+ if (!k)
+ continue;
+
+ mask = 1 << i;
+
+ if (!((priv->last_keys ^ keys0) & mask))
+ continue;
+
+ if ((keys1 | keys0) & mask) {
+ input_event(priv->input, EV_KEY, k, 1);
+ priv->last_keys |= mask;
+ }
+
+ if (!(keys1 & mask)) {
+ input_event(priv->input, EV_KEY, k, 0);
+ priv->last_keys &= ~mask;
+ }
+
+ }
+ input_sync(priv->input);
+
+ return IRQ_HANDLED;
+}
+
+#define res_size(res) ((res)->end - (res)->start + 1)
+
+static int __devinit sh_keysc_probe(struct platform_device *pdev)
+{
+ struct sh_keysc_priv *priv;
+ struct sh_keysc_info *pdata;
+ struct resource *res;
+ struct input_dev *input;
+ int i, k;
+ int irq, error;
+
+ if (!pdev->dev.platform_data) {
+ dev_err(&pdev->dev, "no platform data defined\n");
+ error = -EINVAL;
+ goto err0;
+ }
+
+ error = -ENXIO;
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (res == NULL) {
+ dev_err(&pdev->dev, "failed to get I/O memory\n");
+ goto err0;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "failed to get irq\n");
+ goto err0;
+ }
+
+ priv = kzalloc(sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL) {
+ dev_err(&pdev->dev, "failed to allocate driver data\n");
+ error = -ENOMEM;
+ goto err0;
+ }
+
+ platform_set_drvdata(pdev, priv);
+ memcpy(&priv->pdata, pdev->dev.platform_data, sizeof(priv->pdata));
+ pdata = &priv->pdata;
+
+ priv->iomem_base = ioremap_nocache(res->start, res_size(res));
+ if (priv->iomem_base == NULL) {
+ dev_err(&pdev->dev, "failed to remap I/O memory\n");
+ error = -ENXIO;
+ goto err1;
+ }
+
+ priv->input = input_allocate_device();
+ if (!priv->input) {
+ dev_err(&pdev->dev, "failed to allocate input device\n");
+ error = -ENOMEM;
+ goto err2;
+ }
+
+ input = priv->input;
+ input->evbit[0] = BIT_MASK(EV_KEY);
+
+ input->name = pdev->name;
+ input->phys = "sh-keysc-keys/input0";
+ input->dev.parent = &pdev->dev;
+
+ input->id.bustype = BUS_HOST;
+ input->id.vendor = 0x0001;
+ input->id.product = 0x0001;
+ input->id.version = 0x0100;
+
+ error = request_irq(irq, sh_keysc_isr, 0, pdev->name, pdev);
+ if (error) {
+ dev_err(&pdev->dev, "failed to request IRQ\n");
+ goto err3;
+ }
+
+ for (i = 0; i < SH_KEYSC_MAXKEYS; i++) {
+ k = pdata->keycodes[i];
+ if (k)
+ input_set_capability(input, EV_KEY, k);
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto err4;
+ }
+
+ iowrite16((sh_keysc_mode[pdata->mode].kymd << 8) |
+ pdata->scan_timing, priv->iomem_base + KYCR1_OFFS);
+ iowrite16(0, priv->iomem_base + KYOUTDR_OFFS);
+ iowrite16(KYCR2_IRQ_LEVEL, priv->iomem_base + KYCR2_OFFS);
+ return 0;
+ err4:
+ free_irq(irq, pdev);
+ err3:
+ input_free_device(input);
+ err2:
+ iounmap(priv->iomem_base);
+ err1:
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ err0:
+ return error;
+}
+
+static int __devexit sh_keysc_remove(struct platform_device *pdev)
+{
+ struct sh_keysc_priv *priv = platform_get_drvdata(pdev);
+
+ iowrite16(KYCR2_IRQ_DISABLED, priv->iomem_base + KYCR2_OFFS);
+
+ input_unregister_device(priv->input);
+ free_irq(platform_get_irq(pdev, 0), pdev);
+ iounmap(priv->iomem_base);
+
+ platform_set_drvdata(pdev, NULL);
+ kfree(priv);
+ return 0;
+}
+
+
+#define sh_keysc_suspend NULL
+#define sh_keysc_resume NULL
+
+struct platform_driver sh_keysc_device_driver = {
+ .probe = sh_keysc_probe,
+ .remove = __devexit_p(sh_keysc_remove),
+ .suspend = sh_keysc_suspend,
+ .resume = sh_keysc_resume,
+ .driver = {
+ .name = "sh_keysc",
+ }
+};
+
+static int __init sh_keysc_init(void)
+{
+ return platform_driver_register(&sh_keysc_device_driver);
+}
+
+static void __exit sh_keysc_exit(void)
+{
+ platform_driver_unregister(&sh_keysc_device_driver);
+}
+
+module_init(sh_keysc_init);
+module_exit(sh_keysc_exit);
+
+MODULE_AUTHOR("Magnus Damm");
+MODULE_DESCRIPTION("SuperH KEYSC Keypad Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/spitzkbd.c b/drivers/input/keyboard/spitzkbd.c
new file mode 100644
index 0000000..c48b76a
--- /dev/null
+++ b/drivers/input/keyboard/spitzkbd.c
@@ -0,0 +1,498 @@
+/*
+ * Keyboard driver for Sharp Spitz, Borzoi and Akita (SL-Cxx00 series)
+ *
+ * Copyright (c) 2005 Richard Purdie
+ *
+ * Based on corgikbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+#include <linux/jiffies.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+
+#include <mach/spitz.h>
+#include <mach/hardware.h>
+#include <mach/pxa-regs.h>
+#include <mach/pxa2xx-gpio.h>
+
+#define KB_ROWS 7
+#define KB_COLS 11
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r,c) (((r)<<4) + (c) + 1)
+#define NR_SCANCODES ((KB_ROWS<<4) + 1)
+
+#define SCAN_INTERVAL (50) /* ms */
+#define HINGE_SCAN_INTERVAL (150) /* ms */
+
+#define SPITZ_KEY_CALENDER KEY_F1
+#define SPITZ_KEY_ADDRESS KEY_F2
+#define SPITZ_KEY_FN KEY_F3
+#define SPITZ_KEY_CANCEL KEY_F4
+#define SPITZ_KEY_EXOK KEY_F5
+#define SPITZ_KEY_EXCANCEL KEY_F6
+#define SPITZ_KEY_EXJOGDOWN KEY_F7
+#define SPITZ_KEY_EXJOGUP KEY_F8
+#define SPITZ_KEY_JAP1 KEY_LEFTALT
+#define SPITZ_KEY_JAP2 KEY_RIGHTCTRL
+#define SPITZ_KEY_SYNC KEY_F9
+#define SPITZ_KEY_MAIL KEY_F10
+#define SPITZ_KEY_OK KEY_F11
+#define SPITZ_KEY_MENU KEY_F12
+
+static unsigned char spitzkbd_keycode[NR_SCANCODES] = {
+ 0, /* 0 */
+ KEY_LEFTCTRL, KEY_1, KEY_3, KEY_5, KEY_6, KEY_7, KEY_9, KEY_0, KEY_BACKSPACE, SPITZ_KEY_EXOK, SPITZ_KEY_EXCANCEL, 0, 0, 0, 0, 0, /* 1-16 */
+ 0, KEY_2, KEY_4, KEY_R, KEY_Y, KEY_8, KEY_I, KEY_O, KEY_P, SPITZ_KEY_EXJOGDOWN, SPITZ_KEY_EXJOGUP, 0, 0, 0, 0, 0, /* 17-32 */
+ KEY_TAB, KEY_Q, KEY_E, KEY_T, KEY_G, KEY_U, KEY_J, KEY_K, 0, 0, 0, 0, 0, 0, 0, 0, /* 33-48 */
+ SPITZ_KEY_ADDRESS, KEY_W, KEY_S, KEY_F, KEY_V, KEY_H, KEY_M, KEY_L, 0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0, /* 49-64 */
+ SPITZ_KEY_CALENDER, KEY_A, KEY_D, KEY_C, KEY_B, KEY_N, KEY_DOT, 0, KEY_ENTER, KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, /* 65-80 */
+ SPITZ_KEY_MAIL, KEY_Z, KEY_X, KEY_MINUS, KEY_SPACE, KEY_COMMA, 0, KEY_UP, 0, 0, SPITZ_KEY_FN, 0, 0, 0, 0, 0, /* 81-96 */
+ KEY_SYSRQ, SPITZ_KEY_JAP1, SPITZ_KEY_JAP2, SPITZ_KEY_CANCEL, SPITZ_KEY_OK, SPITZ_KEY_MENU, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0, 0, 0, 0, 0 /* 97-112 */
+};
+
+static int spitz_strobes[] = {
+ SPITZ_GPIO_KEY_STROBE0,
+ SPITZ_GPIO_KEY_STROBE1,
+ SPITZ_GPIO_KEY_STROBE2,
+ SPITZ_GPIO_KEY_STROBE3,
+ SPITZ_GPIO_KEY_STROBE4,
+ SPITZ_GPIO_KEY_STROBE5,
+ SPITZ_GPIO_KEY_STROBE6,
+ SPITZ_GPIO_KEY_STROBE7,
+ SPITZ_GPIO_KEY_STROBE8,
+ SPITZ_GPIO_KEY_STROBE9,
+ SPITZ_GPIO_KEY_STROBE10,
+};
+
+static int spitz_senses[] = {
+ SPITZ_GPIO_KEY_SENSE0,
+ SPITZ_GPIO_KEY_SENSE1,
+ SPITZ_GPIO_KEY_SENSE2,
+ SPITZ_GPIO_KEY_SENSE3,
+ SPITZ_GPIO_KEY_SENSE4,
+ SPITZ_GPIO_KEY_SENSE5,
+ SPITZ_GPIO_KEY_SENSE6,
+};
+
+struct spitzkbd {
+ unsigned char keycode[ARRAY_SIZE(spitzkbd_keycode)];
+ struct input_dev *input;
+ char phys[32];
+
+ spinlock_t lock;
+ struct timer_list timer;
+ struct timer_list htimer;
+
+ unsigned int suspended;
+ unsigned long suspend_jiffies;
+};
+
+#define KB_DISCHARGE_DELAY 10
+#define KB_ACTIVATE_DELAY 10
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using the generic gpio functions to alter
+ * GPDR but it requires a function call per GPIO bit which is
+ * excessive when we need to access 11 bits at once, multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+static inline void spitzkbd_discharge_all(void)
+{
+ /* STROBE All HiZ */
+ GPCR0 = SPITZ_GPIO_G0_STROBE_BIT;
+ GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
+ GPCR1 = SPITZ_GPIO_G1_STROBE_BIT;
+ GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
+ GPCR2 = SPITZ_GPIO_G2_STROBE_BIT;
+ GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
+ GPCR3 = SPITZ_GPIO_G3_STROBE_BIT;
+ GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
+}
+
+static inline void spitzkbd_activate_all(void)
+{
+ /* STROBE ALL -> High */
+ GPSR0 = SPITZ_GPIO_G0_STROBE_BIT;
+ GPDR0 |= SPITZ_GPIO_G0_STROBE_BIT;
+ GPSR1 = SPITZ_GPIO_G1_STROBE_BIT;
+ GPDR1 |= SPITZ_GPIO_G1_STROBE_BIT;
+ GPSR2 = SPITZ_GPIO_G2_STROBE_BIT;
+ GPDR2 |= SPITZ_GPIO_G2_STROBE_BIT;
+ GPSR3 = SPITZ_GPIO_G3_STROBE_BIT;
+ GPDR3 |= SPITZ_GPIO_G3_STROBE_BIT;
+
+ udelay(KB_DISCHARGE_DELAY);
+
+ /* Clear any interrupts we may have triggered when altering the GPIO lines */
+ GEDR0 = SPITZ_GPIO_G0_SENSE_BIT;
+ GEDR1 = SPITZ_GPIO_G1_SENSE_BIT;
+ GEDR2 = SPITZ_GPIO_G2_SENSE_BIT;
+ GEDR3 = SPITZ_GPIO_G3_SENSE_BIT;
+}
+
+static inline void spitzkbd_activate_col(int col)
+{
+ int gpio = spitz_strobes[col];
+ GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
+ GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
+ GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
+ GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
+ GPSR(gpio) = GPIO_bit(gpio);
+ GPDR(gpio) |= GPIO_bit(gpio);
+}
+
+static inline void spitzkbd_reset_col(int col)
+{
+ int gpio = spitz_strobes[col];
+ GPDR0 &= ~SPITZ_GPIO_G0_STROBE_BIT;
+ GPDR1 &= ~SPITZ_GPIO_G1_STROBE_BIT;
+ GPDR2 &= ~SPITZ_GPIO_G2_STROBE_BIT;
+ GPDR3 &= ~SPITZ_GPIO_G3_STROBE_BIT;
+ GPCR(gpio) = GPIO_bit(gpio);
+ GPDR(gpio) |= GPIO_bit(gpio);
+}
+
+static inline int spitzkbd_get_row_status(int col)
+{
+ return ((GPLR0 >> 12) & 0x01) | ((GPLR0 >> 16) & 0x02)
+ | ((GPLR2 >> 25) & 0x04) | ((GPLR1 << 1) & 0x08)
+ | ((GPLR1 >> 0) & 0x10) | ((GPLR1 >> 1) & 0x60);
+}
+
+/*
+ * The spitz keyboard only generates interrupts when a key is pressed.
+ * When a key is pressed, we enable a timer which then scans the
+ * keyboard to detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void spitzkbd_scankeyboard(struct spitzkbd *spitzkbd_data)
+{
+ unsigned int row, col, rowd;
+ unsigned long flags;
+ unsigned int num_pressed, pwrkey = ((GPLR(SPITZ_GPIO_ON_KEY) & GPIO_bit(SPITZ_GPIO_ON_KEY)) != 0);
+
+ if (spitzkbd_data->suspended)
+ return;
+
+ spin_lock_irqsave(&spitzkbd_data->lock, flags);
+
+ num_pressed = 0;
+ for (col = 0; col < KB_COLS; col++) {
+ /*
+ * Discharge the output driver capacitatance
+ * in the keyboard matrix. (Yes it is significant..)
+ */
+
+ spitzkbd_discharge_all();
+ udelay(KB_DISCHARGE_DELAY);
+
+ spitzkbd_activate_col(col);
+ udelay(KB_ACTIVATE_DELAY);
+
+ rowd = spitzkbd_get_row_status(col);
+ for (row = 0; row < KB_ROWS; row++) {
+ unsigned int scancode, pressed;
+
+ scancode = SCANCODE(row, col);
+ pressed = rowd & KB_ROWMASK(row);
+
+ input_report_key(spitzkbd_data->input, spitzkbd_data->keycode[scancode], pressed);
+
+ if (pressed)
+ num_pressed++;
+ }
+ spitzkbd_reset_col(col);
+ }
+
+ spitzkbd_activate_all();
+
+ input_report_key(spitzkbd_data->input, SPITZ_KEY_SYNC, (GPLR(SPITZ_GPIO_SYNC) & GPIO_bit(SPITZ_GPIO_SYNC)) != 0 );
+ input_report_key(spitzkbd_data->input, KEY_SUSPEND, pwrkey);
+
+ if (pwrkey && time_after(jiffies, spitzkbd_data->suspend_jiffies + msecs_to_jiffies(1000))) {
+ input_event(spitzkbd_data->input, EV_PWR, KEY_SUSPEND, 1);
+ spitzkbd_data->suspend_jiffies = jiffies;
+ }
+
+ input_sync(spitzkbd_data->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&spitzkbd_data->timer, jiffies + msecs_to_jiffies(SCAN_INTERVAL));
+
+ spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
+}
+
+/*
+ * spitz keyboard interrupt handler.
+ */
+static irqreturn_t spitzkbd_interrupt(int irq, void *dev_id)
+{
+ struct spitzkbd *spitzkbd_data = dev_id;
+
+ if (!timer_pending(&spitzkbd_data->timer)) {
+ /** wait chattering delay **/
+ udelay(20);
+ spitzkbd_scankeyboard(spitzkbd_data);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * spitz timer checking for released keys
+ */
+static void spitzkbd_timer_callback(unsigned long data)
+{
+ struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
+
+ spitzkbd_scankeyboard(spitzkbd_data);
+}
+
+/*
+ * The hinge switches generate an interrupt.
+ * We debounce the switches and pass them to the input system.
+ */
+
+static irqreturn_t spitzkbd_hinge_isr(int irq, void *dev_id)
+{
+ struct spitzkbd *spitzkbd_data = dev_id;
+
+ if (!timer_pending(&spitzkbd_data->htimer))
+ mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+
+ return IRQ_HANDLED;
+}
+
+#define HINGE_STABLE_COUNT 2
+static int sharpsl_hinge_state;
+static int hinge_count;
+
+static void spitzkbd_hinge_timer(unsigned long data)
+{
+ struct spitzkbd *spitzkbd_data = (struct spitzkbd *) data;
+ unsigned long state;
+ unsigned long flags;
+
+ state = GPLR(SPITZ_GPIO_SWA) & (GPIO_bit(SPITZ_GPIO_SWA)|GPIO_bit(SPITZ_GPIO_SWB));
+ state |= (GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT));
+ if (state != sharpsl_hinge_state) {
+ hinge_count = 0;
+ sharpsl_hinge_state = state;
+ } else if (hinge_count < HINGE_STABLE_COUNT) {
+ hinge_count++;
+ }
+
+ if (hinge_count >= HINGE_STABLE_COUNT) {
+ spin_lock_irqsave(&spitzkbd_data->lock, flags);
+
+ input_report_switch(spitzkbd_data->input, SW_LID, ((GPLR(SPITZ_GPIO_SWA) & GPIO_bit(SPITZ_GPIO_SWA)) != 0));
+ input_report_switch(spitzkbd_data->input, SW_TABLET_MODE, ((GPLR(SPITZ_GPIO_SWB) & GPIO_bit(SPITZ_GPIO_SWB)) != 0));
+ input_report_switch(spitzkbd_data->input, SW_HEADPHONE_INSERT, ((GPLR(SPITZ_GPIO_AK_INT) & GPIO_bit(SPITZ_GPIO_AK_INT)) != 0));
+ input_sync(spitzkbd_data->input);
+
+ spin_unlock_irqrestore(&spitzkbd_data->lock, flags);
+ } else {
+ mod_timer(&spitzkbd_data->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+ }
+}
+
+#ifdef CONFIG_PM
+static int spitzkbd_suspend(struct platform_device *dev, pm_message_t state)
+{
+ int i;
+ struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
+ spitzkbd->suspended = 1;
+
+ /* Set Strobe lines as inputs - *except* strobe line 0 leave this
+ enabled so we can detect a power button press for resume */
+ for (i = 1; i < SPITZ_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(spitz_strobes[i] | GPIO_IN);
+
+ return 0;
+}
+
+static int spitzkbd_resume(struct platform_device *dev)
+{
+ int i;
+ struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
+
+ for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ /* Upon resume, ignore the suspend key for a short while */
+ spitzkbd->suspend_jiffies = jiffies;
+ spitzkbd->suspended = 0;
+
+ return 0;
+}
+#else
+#define spitzkbd_suspend NULL
+#define spitzkbd_resume NULL
+#endif
+
+static int __init spitzkbd_probe(struct platform_device *dev)
+{
+ struct spitzkbd *spitzkbd;
+ struct input_dev *input_dev;
+ int i, err = -ENOMEM;
+
+ spitzkbd = kzalloc(sizeof(struct spitzkbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!spitzkbd || !input_dev)
+ goto fail;
+
+ platform_set_drvdata(dev, spitzkbd);
+ strcpy(spitzkbd->phys, "spitzkbd/input0");
+
+ spin_lock_init(&spitzkbd->lock);
+
+ /* Init Keyboard rescan timer */
+ init_timer(&spitzkbd->timer);
+ spitzkbd->timer.function = spitzkbd_timer_callback;
+ spitzkbd->timer.data = (unsigned long) spitzkbd;
+
+ /* Init Hinge Timer */
+ init_timer(&spitzkbd->htimer);
+ spitzkbd->htimer.function = spitzkbd_hinge_timer;
+ spitzkbd->htimer.data = (unsigned long) spitzkbd;
+
+ spitzkbd->suspend_jiffies = jiffies;
+
+ spitzkbd->input = input_dev;
+
+ input_dev->name = "Spitz Keyboard";
+ input_dev->phys = spitzkbd->phys;
+ input_dev->dev.parent = &dev->dev;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP) |
+ BIT_MASK(EV_PWR) | BIT_MASK(EV_SW);
+ input_dev->keycode = spitzkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(spitzkbd_keycode);
+
+ memcpy(spitzkbd->keycode, spitzkbd_keycode, sizeof(spitzkbd->keycode));
+ for (i = 0; i < ARRAY_SIZE(spitzkbd_keycode); i++)
+ set_bit(spitzkbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+ set_bit(KEY_SUSPEND, input_dev->keybit);
+ set_bit(SW_LID, input_dev->swbit);
+ set_bit(SW_TABLET_MODE, input_dev->swbit);
+ set_bit(SW_HEADPHONE_INSERT, input_dev->swbit);
+
+ err = input_register_device(input_dev);
+ if (err)
+ goto fail;
+
+ mod_timer(&spitzkbd->htimer, jiffies + msecs_to_jiffies(HINGE_SCAN_INTERVAL));
+
+ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++) {
+ pxa_gpio_mode(spitz_senses[i] | GPIO_IN);
+ if (request_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd_interrupt,
+ IRQF_DISABLED|IRQF_TRIGGER_RISING,
+ "Spitzkbd Sense", spitzkbd))
+ printk(KERN_WARNING "spitzkbd: Can't get Sense IRQ: %d!\n", i);
+ }
+
+ /* Set Strobe lines as outputs - set high */
+ for (i = 0; i < SPITZ_KEY_STROBE_NUM; i++)
+ pxa_gpio_mode(spitz_strobes[i] | GPIO_OUT | GPIO_DFLT_HIGH);
+
+ pxa_gpio_mode(SPITZ_GPIO_SYNC | GPIO_IN);
+ pxa_gpio_mode(SPITZ_GPIO_ON_KEY | GPIO_IN);
+ pxa_gpio_mode(SPITZ_GPIO_SWA | GPIO_IN);
+ pxa_gpio_mode(SPITZ_GPIO_SWB | GPIO_IN);
+
+ request_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd Sync", spitzkbd);
+ request_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd PwrOn", spitzkbd);
+ request_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd_hinge_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd SWA", spitzkbd);
+ request_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd_hinge_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd SWB", spitzkbd);
+ request_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd_hinge_isr,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING,
+ "Spitzkbd HP", spitzkbd);
+
+ return 0;
+
+ fail: input_free_device(input_dev);
+ kfree(spitzkbd);
+ return err;
+}
+
+static int spitzkbd_remove(struct platform_device *dev)
+{
+ int i;
+ struct spitzkbd *spitzkbd = platform_get_drvdata(dev);
+
+ for (i = 0; i < SPITZ_KEY_SENSE_NUM; i++)
+ free_irq(IRQ_GPIO(spitz_senses[i]), spitzkbd);
+
+ free_irq(SPITZ_IRQ_GPIO_SYNC, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_ON_KEY, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_SWA, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_SWB, spitzkbd);
+ free_irq(SPITZ_IRQ_GPIO_AK_INT, spitzkbd);
+
+ del_timer_sync(&spitzkbd->htimer);
+ del_timer_sync(&spitzkbd->timer);
+
+ input_unregister_device(spitzkbd->input);
+
+ kfree(spitzkbd);
+
+ return 0;
+}
+
+static struct platform_driver spitzkbd_driver = {
+ .probe = spitzkbd_probe,
+ .remove = spitzkbd_remove,
+ .suspend = spitzkbd_suspend,
+ .resume = spitzkbd_resume,
+ .driver = {
+ .name = "spitz-keyboard",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit spitzkbd_init(void)
+{
+ return platform_driver_register(&spitzkbd_driver);
+}
+
+static void __exit spitzkbd_exit(void)
+{
+ platform_driver_unregister(&spitzkbd_driver);
+}
+
+module_init(spitzkbd_init);
+module_exit(spitzkbd_exit);
+
+MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
+MODULE_DESCRIPTION("Spitz Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:spitz-keyboard");
diff --git a/drivers/input/keyboard/stowaway.c b/drivers/input/keyboard/stowaway.c
new file mode 100644
index 0000000..7437219
--- /dev/null
+++ b/drivers/input/keyboard/stowaway.c
@@ -0,0 +1,184 @@
+/*
+ * Stowaway keyboard driver for Linux
+ */
+
+/*
+ * Copyright (c) 2006 Marek Vasut
+ *
+ * Based on Newton keyboard driver for Linux
+ * by Justin Cormack
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <marek.vasut@gmail.com>, or by paper mail:
+ * Marek Vasut, Liskovecka 559, Frydek-Mistek, 738 01 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "Stowaway keyboard driver"
+
+MODULE_AUTHOR("Marek Vasut <marek.vasut@gmail.com>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define SKBD_KEY_MASK 0x7f
+#define SKBD_RELEASE 0x80
+
+static unsigned char skbd_keycode[128] = {
+ KEY_1, KEY_2, KEY_3, KEY_Z, KEY_4, KEY_5, KEY_6, KEY_7,
+ 0, KEY_Q, KEY_W, KEY_E, KEY_R, KEY_T, KEY_Y, KEY_GRAVE,
+ KEY_X, KEY_A, KEY_S, KEY_D, KEY_F, KEY_G, KEY_H, KEY_SPACE,
+ KEY_CAPSLOCK, KEY_TAB, KEY_LEFTCTRL, 0, 0, 0, 0, 0,
+ 0, 0, 0, KEY_LEFTALT, 0, 0, 0, 0,
+ 0, 0, 0, 0, KEY_C, KEY_V, KEY_B, KEY_N,
+ KEY_MINUS, KEY_EQUAL, KEY_BACKSPACE, KEY_HOME, KEY_8, KEY_9, KEY_0, KEY_ESC,
+ KEY_LEFTBRACE, KEY_RIGHTBRACE, KEY_BACKSLASH, KEY_END, KEY_U, KEY_I, KEY_O, KEY_P,
+ KEY_APOSTROPHE, KEY_ENTER, KEY_PAGEUP,0, KEY_J, KEY_K, KEY_L, KEY_SEMICOLON,
+ KEY_SLASH, KEY_UP, KEY_PAGEDOWN, 0,KEY_M, KEY_COMMA, KEY_DOT, KEY_INSERT,
+ KEY_DELETE, KEY_LEFT, KEY_DOWN, KEY_RIGHT, 0, 0, 0,
+ KEY_LEFTSHIFT, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, KEY_F1, KEY_F2, KEY_F3, KEY_F4, KEY_F5, KEY_F6, KEY_F7,
+ KEY_F8, KEY_F9, KEY_F10, KEY_F11, KEY_F12, 0, 0, 0
+};
+
+struct skbd {
+ unsigned char keycode[128];
+ struct input_dev *dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t skbd_interrupt(struct serio *serio, unsigned char data,
+ unsigned int flags)
+{
+ struct skbd *skbd = serio_get_drvdata(serio);
+ struct input_dev *dev = skbd->dev;
+
+ if (skbd->keycode[data & SKBD_KEY_MASK]) {
+ input_report_key(dev, skbd->keycode[data & SKBD_KEY_MASK],
+ !(data & SKBD_RELEASE));
+ input_sync(dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int skbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct skbd *skbd;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ skbd = kzalloc(sizeof(struct skbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!skbd || !input_dev)
+ goto fail1;
+
+ skbd->serio = serio;
+ skbd->dev = input_dev;
+ snprintf(skbd->phys, sizeof(skbd->phys), "%s/input0", serio->phys);
+ memcpy(skbd->keycode, skbd_keycode, sizeof(skbd->keycode));
+
+ input_dev->name = "Stowaway Keyboard";
+ input_dev->phys = skbd->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_STOWAWAY;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->keycode = skbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(skbd_keycode);
+ for (i = 0; i < ARRAY_SIZE(skbd_keycode); i++)
+ set_bit(skbd_keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ serio_set_drvdata(serio, skbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(skbd->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(skbd);
+ return err;
+}
+
+static void skbd_disconnect(struct serio *serio)
+{
+ struct skbd *skbd = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(skbd->dev);
+ kfree(skbd);
+}
+
+static struct serio_device_id skbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_STOWAWAY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, skbd_serio_ids);
+
+static struct serio_driver skbd_drv = {
+ .driver = {
+ .name = "stowaway",
+ },
+ .description = DRIVER_DESC,
+ .id_table = skbd_serio_ids,
+ .interrupt = skbd_interrupt,
+ .connect = skbd_connect,
+ .disconnect = skbd_disconnect,
+};
+
+static int __init skbd_init(void)
+{
+ return serio_register_driver(&skbd_drv);
+}
+
+static void __exit skbd_exit(void)
+{
+ serio_unregister_driver(&skbd_drv);
+}
+
+module_init(skbd_init);
+module_exit(skbd_exit);
diff --git a/drivers/input/keyboard/sunkbd.c b/drivers/input/keyboard/sunkbd.c
new file mode 100644
index 0000000..9fce6d1
--- /dev/null
+++ b/drivers/input/keyboard/sunkbd.c
@@ -0,0 +1,366 @@
+/*
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * Sun keyboard driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/interrupt.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/serio.h>
+#include <linux/workqueue.h>
+
+#define DRIVER_DESC "Sun keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+static unsigned char sunkbd_keycode[128] = {
+ 0,128,114,129,115, 59, 60, 68, 61, 87, 62, 88, 63,100, 64,112,
+ 65, 66, 67, 56,103,119, 99, 70,105,130,131,108,106, 1, 2, 3,
+ 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 41, 14,110,113, 98, 55,
+ 116,132, 83,133,102, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25,
+ 26, 27,111,127, 71, 72, 73, 74,134,135,107, 0, 29, 30, 31, 32,
+ 33, 34, 35, 36, 37, 38, 39, 40, 43, 28, 96, 75, 76, 77, 82,136,
+ 104,137, 69, 42, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54,101,
+ 79, 80, 81, 0, 0, 0,138, 58,125, 57,126,109, 86, 78
+};
+
+#define SUNKBD_CMD_RESET 0x1
+#define SUNKBD_CMD_BELLON 0x2
+#define SUNKBD_CMD_BELLOFF 0x3
+#define SUNKBD_CMD_CLICK 0xa
+#define SUNKBD_CMD_NOCLICK 0xb
+#define SUNKBD_CMD_SETLED 0xe
+#define SUNKBD_CMD_LAYOUT 0xf
+
+#define SUNKBD_RET_RESET 0xff
+#define SUNKBD_RET_ALLUP 0x7f
+#define SUNKBD_RET_LAYOUT 0xfe
+
+#define SUNKBD_LAYOUT_5_MASK 0x20
+#define SUNKBD_RELEASE 0x80
+#define SUNKBD_KEY 0x7f
+
+/*
+ * Per-keyboard data.
+ */
+
+struct sunkbd {
+ unsigned char keycode[128];
+ struct input_dev *dev;
+ struct serio *serio;
+ struct work_struct tq;
+ wait_queue_head_t wait;
+ char name[64];
+ char phys[32];
+ char type;
+ unsigned char enabled;
+ volatile s8 reset;
+ volatile s8 layout;
+};
+
+/*
+ * sunkbd_interrupt() is called by the low level driver when a character
+ * is received.
+ */
+
+static irqreturn_t sunkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct sunkbd* sunkbd = serio_get_drvdata(serio);
+
+ if (sunkbd->reset <= -1) { /* If cp[i] is 0xff, sunkbd->reset will stay -1. */
+ sunkbd->reset = data; /* The keyboard sends 0xff 0xff 0xID on powerup */
+ wake_up_interruptible(&sunkbd->wait);
+ goto out;
+ }
+
+ if (sunkbd->layout == -1) {
+ sunkbd->layout = data;
+ wake_up_interruptible(&sunkbd->wait);
+ goto out;
+ }
+
+ switch (data) {
+
+ case SUNKBD_RET_RESET:
+ schedule_work(&sunkbd->tq);
+ sunkbd->reset = -1;
+ break;
+
+ case SUNKBD_RET_LAYOUT:
+ sunkbd->layout = -1;
+ break;
+
+ case SUNKBD_RET_ALLUP: /* All keys released */
+ break;
+
+ default:
+ if (!sunkbd->enabled)
+ break;
+
+ if (sunkbd->keycode[data & SUNKBD_KEY]) {
+ input_report_key(sunkbd->dev, sunkbd->keycode[data & SUNKBD_KEY], !(data & SUNKBD_RELEASE));
+ input_sync(sunkbd->dev);
+ } else {
+ printk(KERN_WARNING "sunkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & SUNKBD_KEY, data & SUNKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+out:
+ return IRQ_HANDLED;
+}
+
+/*
+ * sunkbd_event() handles events from the input module.
+ */
+
+static int sunkbd_event(struct input_dev *dev, unsigned int type, unsigned int code, int value)
+{
+ struct sunkbd *sunkbd = input_get_drvdata(dev);
+
+ switch (type) {
+
+ case EV_LED:
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, dev->led) << 3) | (!!test_bit(LED_SCROLLL, dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, dev->led) << 1) | !!test_bit(LED_NUML, dev->led));
+ return 0;
+
+ case EV_SND:
+
+ switch (code) {
+
+ case SND_CLICK:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - value);
+ return 0;
+
+ case SND_BELL:
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - value);
+ return 0;
+ }
+
+ break;
+ }
+
+ return -1;
+}
+
+/*
+ * sunkbd_initialize() checks for a Sun keyboard attached, and determines
+ * its type.
+ */
+
+static int sunkbd_initialize(struct sunkbd *sunkbd)
+{
+ sunkbd->reset = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_RESET);
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+ if (sunkbd->reset < 0)
+ return -1;
+
+ sunkbd->type = sunkbd->reset;
+
+ if (sunkbd->type == 4) { /* Type 4 keyboard */
+ sunkbd->layout = -2;
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_LAYOUT);
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->layout >= 0, HZ/4);
+ if (sunkbd->layout < 0) return -1;
+ if (sunkbd->layout & SUNKBD_LAYOUT_5_MASK) sunkbd->type = 5;
+ }
+
+ return 0;
+}
+
+/*
+ * sunkbd_reinit() sets leds and beeps to a state the computer remembers they
+ * were in.
+ */
+
+static void sunkbd_reinit(struct work_struct *work)
+{
+ struct sunkbd *sunkbd = container_of(work, struct sunkbd, tq);
+
+ wait_event_interruptible_timeout(sunkbd->wait, sunkbd->reset >= 0, HZ);
+
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_SETLED);
+ sunkbd->serio->write(sunkbd->serio,
+ (!!test_bit(LED_CAPSL, sunkbd->dev->led) << 3) | (!!test_bit(LED_SCROLLL, sunkbd->dev->led) << 2) |
+ (!!test_bit(LED_COMPOSE, sunkbd->dev->led) << 1) | !!test_bit(LED_NUML, sunkbd->dev->led));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_NOCLICK - !!test_bit(SND_CLICK, sunkbd->dev->snd));
+ sunkbd->serio->write(sunkbd->serio, SUNKBD_CMD_BELLOFF - !!test_bit(SND_BELL, sunkbd->dev->snd));
+}
+
+static void sunkbd_enable(struct sunkbd *sunkbd, int enable)
+{
+ serio_pause_rx(sunkbd->serio);
+ sunkbd->enabled = enable;
+ serio_continue_rx(sunkbd->serio);
+}
+
+/*
+ * sunkbd_connect() probes for a Sun keyboard and fills the necessary structures.
+ */
+
+static int sunkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct sunkbd *sunkbd;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ sunkbd = kzalloc(sizeof(struct sunkbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!sunkbd || !input_dev)
+ goto fail1;
+
+ sunkbd->serio = serio;
+ sunkbd->dev = input_dev;
+ init_waitqueue_head(&sunkbd->wait);
+ INIT_WORK(&sunkbd->tq, sunkbd_reinit);
+ snprintf(sunkbd->phys, sizeof(sunkbd->phys), "%s/input0", serio->phys);
+
+ serio_set_drvdata(serio, sunkbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ if (sunkbd_initialize(sunkbd) < 0) {
+ err = -ENODEV;
+ goto fail3;
+ }
+
+ snprintf(sunkbd->name, sizeof(sunkbd->name), "Sun Type %d keyboard", sunkbd->type);
+ memcpy(sunkbd->keycode, sunkbd_keycode, sizeof(sunkbd->keycode));
+
+ input_dev->name = sunkbd->name;
+ input_dev->phys = sunkbd->phys;
+ input_dev->id.bustype = BUS_RS232;
+ input_dev->id.vendor = SERIO_SUNKBD;
+ input_dev->id.product = sunkbd->type;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_set_drvdata(input_dev, sunkbd);
+
+ input_dev->event = sunkbd_event;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_LED) |
+ BIT_MASK(EV_SND) | BIT_MASK(EV_REP);
+ input_dev->ledbit[0] = BIT_MASK(LED_CAPSL) | BIT_MASK(LED_COMPOSE) |
+ BIT_MASK(LED_SCROLLL) | BIT_MASK(LED_NUML);
+ input_dev->sndbit[0] = BIT_MASK(SND_CLICK) | BIT_MASK(SND_BELL);
+
+ input_dev->keycode = sunkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(sunkbd_keycode);
+ for (i = 0; i < 128; i++)
+ set_bit(sunkbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ sunkbd_enable(sunkbd, 1);
+
+ err = input_register_device(sunkbd->dev);
+ if (err)
+ goto fail4;
+
+ return 0;
+
+ fail4: sunkbd_enable(sunkbd, 0);
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(sunkbd);
+ return err;
+}
+
+/*
+ * sunkbd_disconnect() unregisters and closes behind us.
+ */
+
+static void sunkbd_disconnect(struct serio *serio)
+{
+ struct sunkbd *sunkbd = serio_get_drvdata(serio);
+
+ sunkbd_enable(sunkbd, 0);
+ input_unregister_device(sunkbd->dev);
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ kfree(sunkbd);
+}
+
+static struct serio_device_id sunkbd_serio_ids[] = {
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_SUNKBD,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ {
+ .type = SERIO_RS232,
+ .proto = SERIO_UNKNOWN, /* sunkbd does probe */
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, sunkbd_serio_ids);
+
+static struct serio_driver sunkbd_drv = {
+ .driver = {
+ .name = "sunkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = sunkbd_serio_ids,
+ .interrupt = sunkbd_interrupt,
+ .connect = sunkbd_connect,
+ .disconnect = sunkbd_disconnect,
+};
+
+/*
+ * The functions for insering/removing us as a module.
+ */
+
+static int __init sunkbd_init(void)
+{
+ return serio_register_driver(&sunkbd_drv);
+}
+
+static void __exit sunkbd_exit(void)
+{
+ serio_unregister_driver(&sunkbd_drv);
+}
+
+module_init(sunkbd_init);
+module_exit(sunkbd_exit);
diff --git a/drivers/input/keyboard/tosakbd.c b/drivers/input/keyboard/tosakbd.c
new file mode 100644
index 0000000..677276b
--- /dev/null
+++ b/drivers/input/keyboard/tosakbd.c
@@ -0,0 +1,430 @@
+/*
+ * Keyboard driver for Sharp Tosa models (SL-6000x)
+ *
+ * Copyright (c) 2005 Dirk Opfer
+ * Copyright (c) 2007 Dmitry Baryshkov
+ *
+ * Based on xtkbd.c/locomkbd.c/corgikbd.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+
+#include <mach/gpio.h>
+#include <mach/tosa.h>
+
+#define KB_ROWMASK(r) (1 << (r))
+#define SCANCODE(r, c) (((r)<<4) + (c) + 1)
+#define NR_SCANCODES SCANCODE(TOSA_KEY_SENSE_NUM - 1, TOSA_KEY_STROBE_NUM - 1) + 1
+
+#define SCAN_INTERVAL (HZ/10)
+
+#define KB_DISCHARGE_DELAY 10
+#define KB_ACTIVATE_DELAY 10
+
+static unsigned int tosakbd_keycode[NR_SCANCODES] = {
+0,
+0, KEY_W, 0, 0, 0, KEY_K, KEY_BACKSPACE, KEY_P,
+0, 0, 0, 0, 0, 0, 0, 0,
+KEY_Q, KEY_E, KEY_T, KEY_Y, 0, KEY_O, KEY_I, KEY_COMMA,
+0, 0, 0, 0, 0, 0, 0, 0,
+KEY_A, KEY_D, KEY_G, KEY_U, 0, KEY_L, KEY_ENTER, KEY_DOT,
+0, 0, 0, 0, 0, 0, 0, 0,
+KEY_Z, KEY_C, KEY_V, KEY_J, TOSA_KEY_ADDRESSBOOK, TOSA_KEY_CANCEL, TOSA_KEY_CENTER, TOSA_KEY_OK,
+KEY_LEFTSHIFT, 0, 0, 0, 0, 0, 0, 0,
+KEY_S, KEY_R, KEY_B, KEY_N, TOSA_KEY_CALENDAR, TOSA_KEY_HOMEPAGE, KEY_LEFTCTRL, TOSA_KEY_LIGHT,
+0, KEY_RIGHTSHIFT, 0, 0, 0, 0, 0, 0,
+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,
+KEY_X, KEY_F, KEY_SPACE, KEY_APOSTROPHE, TOSA_KEY_MAIL, KEY_LEFT, KEY_DOWN, KEY_RIGHT,
+0, 0, 0,
+};
+
+struct tosakbd {
+ unsigned int keycode[ARRAY_SIZE(tosakbd_keycode)];
+ struct input_dev *input;
+ int suspended;
+ spinlock_t lock; /* protect kbd scanning */
+ struct timer_list timer;
+};
+
+
+/* Helper functions for reading the keyboard matrix
+ * Note: We should really be using the generic gpio functions to alter
+ * GPDR but it requires a function call per GPIO bit which is
+ * excessive when we need to access 12 bits at once, multiple times.
+ * These functions must be called within local_irq_save()/local_irq_restore()
+ * or similar.
+ */
+#define GET_ROWS_STATUS(c) ((GPLR2 & TOSA_GPIO_ALL_SENSE_BIT) >> TOSA_GPIO_ALL_SENSE_RSHIFT)
+
+static inline void tosakbd_discharge_all(void)
+{
+ /* STROBE All HiZ */
+ GPCR1 = TOSA_GPIO_HIGH_STROBE_BIT;
+ GPDR1 &= ~TOSA_GPIO_HIGH_STROBE_BIT;
+ GPCR2 = TOSA_GPIO_LOW_STROBE_BIT;
+ GPDR2 &= ~TOSA_GPIO_LOW_STROBE_BIT;
+}
+
+static inline void tosakbd_activate_all(void)
+{
+ /* STROBE ALL -> High */
+ GPSR1 = TOSA_GPIO_HIGH_STROBE_BIT;
+ GPDR1 |= TOSA_GPIO_HIGH_STROBE_BIT;
+ GPSR2 = TOSA_GPIO_LOW_STROBE_BIT;
+ GPDR2 |= TOSA_GPIO_LOW_STROBE_BIT;
+
+ udelay(KB_DISCHARGE_DELAY);
+
+ /* STATE CLEAR */
+ GEDR2 |= TOSA_GPIO_ALL_SENSE_BIT;
+}
+
+static inline void tosakbd_activate_col(int col)
+{
+ if (col <= 5) {
+ /* STROBE col -> High, not col -> HiZ */
+ GPSR1 = TOSA_GPIO_STROBE_BIT(col);
+ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
+ } else {
+ /* STROBE col -> High, not col -> HiZ */
+ GPSR2 = TOSA_GPIO_STROBE_BIT(col);
+ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
+ }
+}
+
+static inline void tosakbd_reset_col(int col)
+{
+ if (col <= 5) {
+ /* STROBE col -> Low */
+ GPCR1 = TOSA_GPIO_STROBE_BIT(col);
+ /* STROBE col -> out, not col -> HiZ */
+ GPDR1 = (GPDR1 & ~TOSA_GPIO_HIGH_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
+ } else {
+ /* STROBE col -> Low */
+ GPCR2 = TOSA_GPIO_STROBE_BIT(col);
+ /* STROBE col -> out, not col -> HiZ */
+ GPDR2 = (GPDR2 & ~TOSA_GPIO_LOW_STROBE_BIT) | TOSA_GPIO_STROBE_BIT(col);
+ }
+}
+/*
+ * The tosa keyboard only generates interrupts when a key is pressed.
+ * So when a key is pressed, we enable a timer. This timer scans the
+ * keyboard, and this is how we detect when the key is released.
+ */
+
+/* Scan the hardware keyboard and push any changes up through the input layer */
+static void tosakbd_scankeyboard(struct platform_device *dev)
+{
+ struct tosakbd *tosakbd = platform_get_drvdata(dev);
+ unsigned int row, col, rowd;
+ unsigned long flags;
+ unsigned int num_pressed = 0;
+
+ spin_lock_irqsave(&tosakbd->lock, flags);
+
+ if (tosakbd->suspended)
+ goto out;
+
+ for (col = 0; col < TOSA_KEY_STROBE_NUM; col++) {
+ /*
+ * Discharge the output driver capacitatance
+ * in the keyboard matrix. (Yes it is significant..)
+ */
+ tosakbd_discharge_all();
+ udelay(KB_DISCHARGE_DELAY);
+
+ tosakbd_activate_col(col);
+ udelay(KB_ACTIVATE_DELAY);
+
+ rowd = GET_ROWS_STATUS(col);
+
+ for (row = 0; row < TOSA_KEY_SENSE_NUM; row++) {
+ unsigned int scancode, pressed;
+ scancode = SCANCODE(row, col);
+ pressed = rowd & KB_ROWMASK(row);
+
+ if (pressed && !tosakbd->keycode[scancode])
+ dev_warn(&dev->dev,
+ "unhandled scancode: 0x%02x\n",
+ scancode);
+
+ input_report_key(tosakbd->input,
+ tosakbd->keycode[scancode],
+ pressed);
+ if (pressed)
+ num_pressed++;
+ }
+
+ tosakbd_reset_col(col);
+ }
+
+ tosakbd_activate_all();
+
+ input_sync(tosakbd->input);
+
+ /* if any keys are pressed, enable the timer */
+ if (num_pressed)
+ mod_timer(&tosakbd->timer, jiffies + SCAN_INTERVAL);
+
+ out:
+ spin_unlock_irqrestore(&tosakbd->lock, flags);
+}
+
+/*
+ * tosa keyboard interrupt handler.
+ */
+static irqreturn_t tosakbd_interrupt(int irq, void *__dev)
+{
+ struct platform_device *dev = __dev;
+ struct tosakbd *tosakbd = platform_get_drvdata(dev);
+
+ if (!timer_pending(&tosakbd->timer)) {
+ /** wait chattering delay **/
+ udelay(20);
+ tosakbd_scankeyboard(dev);
+ }
+
+ return IRQ_HANDLED;
+}
+
+/*
+ * tosa timer checking for released keys
+ */
+static void tosakbd_timer_callback(unsigned long __dev)
+{
+ struct platform_device *dev = (struct platform_device *)__dev;
+
+ tosakbd_scankeyboard(dev);
+}
+
+#ifdef CONFIG_PM
+static int tosakbd_suspend(struct platform_device *dev, pm_message_t state)
+{
+ struct tosakbd *tosakbd = platform_get_drvdata(dev);
+ unsigned long flags;
+
+ spin_lock_irqsave(&tosakbd->lock, flags);
+ tosakbd->suspended = 1;
+ spin_unlock_irqrestore(&tosakbd->lock, flags);
+
+ del_timer_sync(&tosakbd->timer);
+
+ return 0;
+}
+
+static int tosakbd_resume(struct platform_device *dev)
+{
+ struct tosakbd *tosakbd = platform_get_drvdata(dev);
+
+ tosakbd->suspended = 0;
+ tosakbd_scankeyboard(dev);
+
+ return 0;
+}
+#else
+#define tosakbd_suspend NULL
+#define tosakbd_resume NULL
+#endif
+
+static int __devinit tosakbd_probe(struct platform_device *pdev) {
+
+ int i;
+ struct tosakbd *tosakbd;
+ struct input_dev *input_dev;
+ int error;
+
+ tosakbd = kzalloc(sizeof(struct tosakbd), GFP_KERNEL);
+ if (!tosakbd)
+ return -ENOMEM;
+
+ input_dev = input_allocate_device();
+ if (!input_dev) {
+ kfree(tosakbd);
+ return -ENOMEM;
+ }
+
+ platform_set_drvdata(pdev, tosakbd);
+
+ spin_lock_init(&tosakbd->lock);
+
+ /* Init Keyboard rescan timer */
+ init_timer(&tosakbd->timer);
+ tosakbd->timer.function = tosakbd_timer_callback;
+ tosakbd->timer.data = (unsigned long) pdev;
+
+ tosakbd->input = input_dev;
+
+ input_set_drvdata(input_dev, tosakbd);
+ input_dev->name = "Tosa Keyboard";
+ input_dev->phys = "tosakbd/input0";
+ input_dev->dev.parent = &pdev->dev;
+
+ input_dev->id.bustype = BUS_HOST;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+
+ input_dev->evbit[0] = BIT(EV_KEY) | BIT(EV_REP);
+ input_dev->keycode = tosakbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned int);
+ input_dev->keycodemax = ARRAY_SIZE(tosakbd_keycode);
+
+ memcpy(tosakbd->keycode, tosakbd_keycode, sizeof(tosakbd_keycode));
+
+ for (i = 0; i < ARRAY_SIZE(tosakbd_keycode); i++)
+ __set_bit(tosakbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ /* Setup sense interrupts - RisingEdge Detect, sense lines as inputs */
+ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
+ int gpio = TOSA_GPIO_KEY_SENSE(i);
+ int irq;
+ error = gpio_request(gpio, "tosakbd");
+ if (error < 0) {
+ printk(KERN_ERR "tosakbd: failed to request GPIO %d, "
+ " error %d\n", gpio, error);
+ goto fail;
+ }
+
+ error = gpio_direction_input(TOSA_GPIO_KEY_SENSE(i));
+ if (error < 0) {
+ printk(KERN_ERR "tosakbd: failed to configure input"
+ " direction for GPIO %d, error %d\n",
+ gpio, error);
+ gpio_free(gpio);
+ goto fail;
+ }
+
+ irq = gpio_to_irq(gpio);
+ if (irq < 0) {
+ error = irq;
+ printk(KERN_ERR "gpio-keys: Unable to get irq number"
+ " for GPIO %d, error %d\n",
+ gpio, error);
+ gpio_free(gpio);
+ goto fail;
+ }
+
+ error = request_irq(irq, tosakbd_interrupt,
+ IRQF_DISABLED | IRQF_TRIGGER_RISING,
+ "tosakbd", pdev);
+
+ if (error) {
+ printk("tosakbd: Can't get IRQ: %d: error %d!\n",
+ irq, error);
+ gpio_free(gpio);
+ goto fail;
+ }
+ }
+
+ /* Set Strobe lines as outputs - set high */
+ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++) {
+ int gpio = TOSA_GPIO_KEY_STROBE(i);
+ error = gpio_request(gpio, "tosakbd");
+ if (error < 0) {
+ printk(KERN_ERR "tosakbd: failed to request GPIO %d, "
+ " error %d\n", gpio, error);
+ goto fail2;
+ }
+
+ error = gpio_direction_output(gpio, 1);
+ if (error < 0) {
+ printk(KERN_ERR "tosakbd: failed to configure input"
+ " direction for GPIO %d, error %d\n",
+ gpio, error);
+ gpio_free(gpio);
+ goto fail;
+ }
+
+ }
+
+ error = input_register_device(input_dev);
+ if (error) {
+ printk(KERN_ERR "tosakbd: Unable to register input device, "
+ "error: %d\n", error);
+ goto fail;
+ }
+
+ printk(KERN_INFO "input: Tosa Keyboard Registered\n");
+
+ return 0;
+
+fail2:
+ while (--i >= 0)
+ gpio_free(TOSA_GPIO_KEY_STROBE(i));
+
+ i = TOSA_KEY_SENSE_NUM;
+fail:
+ while (--i >= 0) {
+ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), pdev);
+ gpio_free(TOSA_GPIO_KEY_SENSE(i));
+ }
+
+ platform_set_drvdata(pdev, NULL);
+ input_free_device(input_dev);
+ kfree(tosakbd);
+
+ return error;
+}
+
+static int __devexit tosakbd_remove(struct platform_device *dev)
+{
+ int i;
+ struct tosakbd *tosakbd = platform_get_drvdata(dev);
+
+ for (i = 0; i < TOSA_KEY_STROBE_NUM; i++)
+ gpio_free(TOSA_GPIO_KEY_STROBE(i));
+
+ for (i = 0; i < TOSA_KEY_SENSE_NUM; i++) {
+ free_irq(gpio_to_irq(TOSA_GPIO_KEY_SENSE(i)), dev);
+ gpio_free(TOSA_GPIO_KEY_SENSE(i));
+ }
+
+ del_timer_sync(&tosakbd->timer);
+
+ input_unregister_device(tosakbd->input);
+
+ kfree(tosakbd);
+
+ return 0;
+}
+
+static struct platform_driver tosakbd_driver = {
+ .probe = tosakbd_probe,
+ .remove = __devexit_p(tosakbd_remove),
+ .suspend = tosakbd_suspend,
+ .resume = tosakbd_resume,
+ .driver = {
+ .name = "tosa-keyboard",
+ .owner = THIS_MODULE,
+ },
+};
+
+static int __devinit tosakbd_init(void)
+{
+ return platform_driver_register(&tosakbd_driver);
+}
+
+static void __exit tosakbd_exit(void)
+{
+ platform_driver_unregister(&tosakbd_driver);
+}
+
+module_init(tosakbd_init);
+module_exit(tosakbd_exit);
+
+MODULE_AUTHOR("Dirk Opfer <Dirk@Opfer-Online.de>");
+MODULE_DESCRIPTION("Tosa Keyboard Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:tosa-keyboard");
diff --git a/drivers/input/keyboard/xtkbd.c b/drivers/input/keyboard/xtkbd.c
new file mode 100644
index 0000000..37b01d7
--- /dev/null
+++ b/drivers/input/keyboard/xtkbd.c
@@ -0,0 +1,183 @@
+/*
+ * Copyright (c) 1999-2001 Vojtech Pavlik
+ */
+
+/*
+ * XT keyboard driver for Linux
+ */
+
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ *
+ * Should you need to contact me, the author, you can do so either by
+ * e-mail - mail your message to <vojtech@ucw.cz>, or by paper mail:
+ * Vojtech Pavlik, Simunkova 1594, Prague 8, 182 00 Czech Republic
+ */
+
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/serio.h>
+
+#define DRIVER_DESC "XT keyboard driver"
+
+MODULE_AUTHOR("Vojtech Pavlik <vojtech@ucw.cz>");
+MODULE_DESCRIPTION(DRIVER_DESC);
+MODULE_LICENSE("GPL");
+
+#define XTKBD_EMUL0 0xe0
+#define XTKBD_EMUL1 0xe1
+#define XTKBD_KEY 0x7f
+#define XTKBD_RELEASE 0x80
+
+static unsigned char xtkbd_keycode[256] = {
+ 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15,
+ 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+ 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47,
+ 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
+ 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
+ 80, 81, 82, 83, 0, 0, 0, 87, 88, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 87, 88, 0, 0, 0, 0,110,111,103,108,105,
+ 106
+};
+
+struct xtkbd {
+ unsigned char keycode[256];
+ struct input_dev *dev;
+ struct serio *serio;
+ char phys[32];
+};
+
+static irqreturn_t xtkbd_interrupt(struct serio *serio,
+ unsigned char data, unsigned int flags)
+{
+ struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+ switch (data) {
+ case XTKBD_EMUL0:
+ case XTKBD_EMUL1:
+ break;
+ default:
+
+ if (xtkbd->keycode[data & XTKBD_KEY]) {
+ input_report_key(xtkbd->dev, xtkbd->keycode[data & XTKBD_KEY], !(data & XTKBD_RELEASE));
+ input_sync(xtkbd->dev);
+ } else {
+ printk(KERN_WARNING "xtkbd.c: Unknown key (scancode %#x) %s.\n",
+ data & XTKBD_KEY, data & XTKBD_RELEASE ? "released" : "pressed");
+ }
+ }
+ return IRQ_HANDLED;
+}
+
+static int xtkbd_connect(struct serio *serio, struct serio_driver *drv)
+{
+ struct xtkbd *xtkbd;
+ struct input_dev *input_dev;
+ int err = -ENOMEM;
+ int i;
+
+ xtkbd = kmalloc(sizeof(struct xtkbd), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!xtkbd || !input_dev)
+ goto fail1;
+
+ xtkbd->serio = serio;
+ xtkbd->dev = input_dev;
+ snprintf(xtkbd->phys, sizeof(xtkbd->phys), "%s/input0", serio->phys);
+ memcpy(xtkbd->keycode, xtkbd_keycode, sizeof(xtkbd->keycode));
+
+ input_dev->name = "XT Keyboard";
+ input_dev->phys = xtkbd->phys;
+ input_dev->id.bustype = BUS_XTKBD;
+ input_dev->id.vendor = 0x0001;
+ input_dev->id.product = 0x0001;
+ input_dev->id.version = 0x0100;
+ input_dev->dev.parent = &serio->dev;
+
+ input_dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+ input_dev->keycode = xtkbd->keycode;
+ input_dev->keycodesize = sizeof(unsigned char);
+ input_dev->keycodemax = ARRAY_SIZE(xtkbd_keycode);
+
+ for (i = 0; i < 255; i++)
+ set_bit(xtkbd->keycode[i], input_dev->keybit);
+ clear_bit(0, input_dev->keybit);
+
+ serio_set_drvdata(serio, xtkbd);
+
+ err = serio_open(serio, drv);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(xtkbd->dev);
+ if (err)
+ goto fail3;
+
+ return 0;
+
+ fail3: serio_close(serio);
+ fail2: serio_set_drvdata(serio, NULL);
+ fail1: input_free_device(input_dev);
+ kfree(xtkbd);
+ return err;
+}
+
+static void xtkbd_disconnect(struct serio *serio)
+{
+ struct xtkbd *xtkbd = serio_get_drvdata(serio);
+
+ serio_close(serio);
+ serio_set_drvdata(serio, NULL);
+ input_unregister_device(xtkbd->dev);
+ kfree(xtkbd);
+}
+
+static struct serio_device_id xtkbd_serio_ids[] = {
+ {
+ .type = SERIO_XT,
+ .proto = SERIO_ANY,
+ .id = SERIO_ANY,
+ .extra = SERIO_ANY,
+ },
+ { 0 }
+};
+
+MODULE_DEVICE_TABLE(serio, xtkbd_serio_ids);
+
+static struct serio_driver xtkbd_drv = {
+ .driver = {
+ .name = "xtkbd",
+ },
+ .description = DRIVER_DESC,
+ .id_table = xtkbd_serio_ids,
+ .interrupt = xtkbd_interrupt,
+ .connect = xtkbd_connect,
+ .disconnect = xtkbd_disconnect,
+};
+
+static int __init xtkbd_init(void)
+{
+ return serio_register_driver(&xtkbd_drv);
+}
+
+static void __exit xtkbd_exit(void)
+{
+ serio_unregister_driver(&xtkbd_drv);
+}
+
+module_init(xtkbd_init);
+module_exit(xtkbd_exit);
OpenPOWER on IntegriCloud