Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

input: serio: add support for Amstrad Delta serial keyboardport

The patch introduces a serio driver that supports a keyboard serial port found
on the Amstrad Delta videophone board.

After initializing the hardware, the driver reads its input data from a buffer
filled in by the board FIQ (Fast Interrupt Request) handler.

Standard AT keyboard driver (atkbd) will be used on top of the serio layer for
handling the E3 keyboard (called mailboard) connected to the port. Since the
device generated scancodes differ from what the atkbd expects, a custom key
code to scan code table must be loaded from userspace for the keyboard to be
useable.

Signed-off-by: Janusz Krzysztofik <jkrzyszt@tis.icnet.pl>
Acked-by: Dmitry Torokhov <dtor@mail.ru>
Signed-off-by: Tony Lindgren <tony@atomide.com>

authored by

Janusz Krzysztofik and committed by
Tony Lindgren
29453932 e6f740f5

+194
+16
drivers/input/serio/Kconfig
··· 209 209 To compile this driver as a module, choose M here: the 210 210 module will be called altera_ps2. 211 211 212 + config SERIO_AMS_DELTA 213 + tristate "Amstrad Delta (E3) mailboard support" 214 + depends on MACH_AMS_DELTA 215 + default y 216 + select AMS_DELTA_FIQ 217 + ---help--- 218 + Say Y here if you have an E3 and want to use its mailboard, 219 + or any standard AT keyboard connected to the mailboard port. 220 + 221 + When used for the E3 mailboard, a non-standard key table 222 + must be loaded from userspace, possibly using udev extras 223 + provided keymap helper utility. 224 + 225 + To compile this driver as a module, choose M here; 226 + the module will be called ams_delta_serio. 227 + 212 228 endif
+1
drivers/input/serio/Makefile
··· 21 21 obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o 22 22 obj-$(CONFIG_SERIO_LIBPS2) += libps2.o 23 23 obj-$(CONFIG_SERIO_RAW) += serio_raw.o 24 + obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o 24 25 obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o 25 26 obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o
+177
drivers/input/serio/ams_delta_serio.c
··· 1 + /* 2 + * Amstrad E3 (Delta) keyboard port driver 3 + * 4 + * Copyright (c) 2006 Matt Callow 5 + * Copyright (c) 2010 Janusz Krzysztofik 6 + * 7 + * This program is free software; you can redistribute it and/or modify it 8 + * under the terms of the GNU General Public License version 2 as published by 9 + * the Free Software Foundation. 10 + * 11 + * Thanks to Cliff Lawson for his help 12 + * 13 + * The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial 14 + * transmission. The keyboard port is formed of two GPIO lines, for clock 15 + * and data. Due to strict timing requirements of the interface, 16 + * the serial data stream is read and processed by a FIQ handler. 17 + * The resulting words are fetched by this driver from a circular buffer. 18 + * 19 + * Standard AT keyboard driver (atkbd) is used for handling the keyboard data. 20 + * However, when used with the E3 mailboard that producecs non-standard 21 + * scancodes, a custom key table must be prepared and loaded from userspace. 22 + */ 23 + #include <linux/gpio.h> 24 + #include <linux/irq.h> 25 + #include <linux/serio.h> 26 + #include <linux/slab.h> 27 + 28 + #include <asm/mach-types.h> 29 + #include <plat/board-ams-delta.h> 30 + 31 + #include <mach/ams-delta-fiq.h> 32 + 33 + MODULE_AUTHOR("Matt Callow"); 34 + MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver"); 35 + MODULE_LICENSE("GPL"); 36 + 37 + static struct serio *ams_delta_serio; 38 + 39 + static int check_data(int data) 40 + { 41 + int i, parity = 0; 42 + 43 + /* check valid stop bit */ 44 + if (!(data & 0x400)) { 45 + dev_warn(&ams_delta_serio->dev, 46 + "invalid stop bit, data=0x%X\n", 47 + data); 48 + return SERIO_FRAME; 49 + } 50 + /* calculate the parity */ 51 + for (i = 1; i < 10; i++) { 52 + if (data & (1 << i)) 53 + parity++; 54 + } 55 + /* it should be odd */ 56 + if (!(parity & 0x01)) { 57 + dev_warn(&ams_delta_serio->dev, 58 + "paritiy check failed, data=0x%X parity=0x%X\n", 59 + data, parity); 60 + return SERIO_PARITY; 61 + } 62 + return 0; 63 + } 64 + 65 + static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id) 66 + { 67 + int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF]; 68 + int data, dfl; 69 + u8 scancode; 70 + 71 + fiq_buffer[FIQ_IRQ_PEND] = 0; 72 + 73 + /* 74 + * Read data from the circular buffer, check it 75 + * and then pass it on the serio 76 + */ 77 + while (fiq_buffer[FIQ_KEYS_CNT] > 0) { 78 + 79 + data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++]; 80 + fiq_buffer[FIQ_KEYS_CNT]--; 81 + if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN]) 82 + fiq_buffer[FIQ_HEAD_OFFSET] = 0; 83 + 84 + dfl = check_data(data); 85 + scancode = (u8) (data >> 1) & 0xFF; 86 + serio_interrupt(ams_delta_serio, scancode, dfl); 87 + } 88 + return IRQ_HANDLED; 89 + } 90 + 91 + static int ams_delta_serio_open(struct serio *serio) 92 + { 93 + /* enable keyboard */ 94 + ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 95 + AMD_DELTA_LATCH2_KEYBRD_PWR); 96 + 97 + return 0; 98 + } 99 + 100 + static void ams_delta_serio_close(struct serio *serio) 101 + { 102 + /* disable keyboard */ 103 + ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0); 104 + } 105 + 106 + static int __init ams_delta_serio_init(void) 107 + { 108 + int err; 109 + 110 + if (!machine_is_ams_delta()) 111 + return -ENODEV; 112 + 113 + ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL); 114 + if (!ams_delta_serio) 115 + return -ENOMEM; 116 + 117 + ams_delta_serio->id.type = SERIO_8042; 118 + ams_delta_serio->open = ams_delta_serio_open; 119 + ams_delta_serio->close = ams_delta_serio_close; 120 + strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter", 121 + sizeof(ams_delta_serio->name)); 122 + strlcpy(ams_delta_serio->phys, "GPIO/serio0", 123 + sizeof(ams_delta_serio->phys)); 124 + 125 + err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data"); 126 + if (err) { 127 + pr_err("ams_delta_serio: Couldn't request gpio pin for data\n"); 128 + goto serio; 129 + } 130 + gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); 131 + 132 + err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock"); 133 + if (err) { 134 + pr_err("ams_delta_serio: couldn't request gpio pin for clock\n"); 135 + goto gpio_data; 136 + } 137 + gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); 138 + 139 + err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 140 + ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING, 141 + "ams-delta-serio", 0); 142 + if (err < 0) { 143 + pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n", 144 + gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK)); 145 + goto gpio_clk; 146 + } 147 + /* 148 + * Since GPIO register handling for keyboard clock pin is performed 149 + * at FIQ level, switch back from edge to simple interrupt handler 150 + * to avoid bad interaction. 151 + */ 152 + set_irq_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 153 + handle_simple_irq); 154 + 155 + serio_register_port(ams_delta_serio); 156 + dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name); 157 + 158 + return 0; 159 + gpio_clk: 160 + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); 161 + gpio_data: 162 + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); 163 + serio: 164 + kfree(ams_delta_serio); 165 + return err; 166 + } 167 + module_init(ams_delta_serio_init); 168 + 169 + static void __exit ams_delta_serio_exit(void) 170 + { 171 + serio_unregister_port(ams_delta_serio); 172 + free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0); 173 + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK); 174 + gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA); 175 + kfree(ams_delta_serio); 176 + } 177 + module_exit(ams_delta_serio_exit);