diff options
Diffstat (limited to 'drivers/input/keyboard/amikbd.c')
-rw-r--r-- | drivers/input/keyboard/amikbd.c | 242 |
1 files changed, 242 insertions, 0 deletions
diff --git a/drivers/input/keyboard/amikbd.c b/drivers/input/keyboard/amikbd.c new file mode 100644 index 0000000000..e305c44cd0 --- /dev/null +++ b/drivers/input/keyboard/amikbd.c @@ -0,0 +1,242 @@ +// 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 = devm_input_allocate_device(&pdev->dev); + 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->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 = devm_request_irq(&pdev->dev, IRQ_AMIGA_CIAA_SP, amikbd_interrupt, + 0, "amikbd", dev); + if (err) + return err; + + err = input_register_device(dev); + if (err) + return err; + + platform_set_drvdata(pdev, dev); + + return 0; +} + +static struct platform_driver amikbd_driver = { + .driver = { + .name = "amiga-keyboard", + }, +}; + +module_platform_driver_probe(amikbd_driver, amikbd_probe); + +MODULE_ALIAS("platform:amiga-keyboard"); |