summaryrefslogtreecommitdiffstats
path: root/drivers/input/keyboard/amikbd.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard/amikbd.c')
-rw-r--r--drivers/input/keyboard/amikbd.c257
1 files changed, 257 insertions, 0 deletions
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c
new file mode 100644
index 000000000..a20a4e186
--- /dev/null
+++ b/drivers/input/keyboard/amikbd.c
@@ -0,0 +1,257 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright (c) 2000-2001 Vojtech Pavlik
+ *
+ * Based on the work of:
+ * Hamish Macdonald
+ */
+
+/*
+ * Amiga keyboard driver for Linux/m68k
+ */
+
+#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 <linux/platform_device.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");
+
+#ifdef CONFIG_HW_CONSOLE
+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 void __init amikbd_init_console_keymaps(void)
+{
+ /* We can spare 512 bytes on stack for temp_map in init path. */
+ unsigned short temp_map[NR_KEYS];
+ int i, j;
+
+ for (i = 0; i < MAX_NR_KEYMAPS; i++) {
+ 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));
+ }
+}
+#else /* !CONFIG_HW_CONSOLE */
+static inline void amikbd_init_console_keymaps(void) {}
+#endif /* !CONFIG_HW_CONSOLE */
+
+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 irqreturn_t amikbd_interrupt(int irq, void *data)
+{
+ struct input_dev *dev = data;
+ 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(dev, scancode, 1);
+ input_report_key(dev, scancode, 0);
+ } else {
+ input_report_key(dev, scancode, down);
+ }
+
+ input_sync(dev);
+ } else /* scancodes >= 0x78 are error codes */
+ printk(amikbd_messages[scancode - 0x78]);
+
+ return IRQ_HANDLED;
+}
+
+static int __init amikbd_probe(struct platform_device *pdev)
+{
+ struct input_dev *dev;
+ int i, err;
+
+ dev = input_allocate_device();
+ if (!dev) {
+ dev_err(&pdev->dev, "Not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ dev->name = pdev->name;
+ dev->phys = "amikbd/input0";
+ dev->id.bustype = BUS_AMIGA;
+ dev->id.vendor = 0x0001;
+ dev->id.product = 0x0001;
+ dev->id.version = 0x0100;
+ dev->dev.parent = &pdev->dev;
+
+ dev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_REP);
+
+ for (i = 0; i < 0x78; i++)
+ set_bit(i, dev->keybit);
+
+ amikbd_init_console_keymaps();
+
+ ciaa.cra &= ~0x41; /* serial data in, turn off TA */
+ err = request_irq(IRQ_AMIGA_CIAA_SP, amikbd_interrupt, 0, "amikbd",
+ dev);
+ if (err)
+ goto fail2;
+
+ err = input_register_device(dev);
+ if (err)
+ goto fail3;
+
+ platform_set_drvdata(pdev, dev);
+
+ return 0;
+
+ fail3: free_irq(IRQ_AMIGA_CIAA_SP, dev);
+ fail2: input_free_device(dev);
+ return err;
+}
+
+static int __exit amikbd_remove(struct platform_device *pdev)
+{
+ struct input_dev *dev = platform_get_drvdata(pdev);
+
+ free_irq(IRQ_AMIGA_CIAA_SP, dev);
+ input_unregister_device(dev);
+ return 0;
+}
+
+static struct platform_driver amikbd_driver = {
+ .remove = __exit_p(amikbd_remove),
+ .driver = {
+ .name = "amiga-keyboard",
+ },
+};
+
+module_platform_driver_probe(amikbd_driver, amikbd_probe);
+
+MODULE_ALIAS("platform:amiga-keyboard");