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); 178