Path: blob/master/drivers/input/serio/ams_delta_serio.c
15112 views
/*1* Amstrad E3 (Delta) keyboard port driver2*3* Copyright (c) 2006 Matt Callow4* Copyright (c) 2010 Janusz Krzysztofik5*6* This program is free software; you can redistribute it and/or modify it7* under the terms of the GNU General Public License version 2 as published by8* the Free Software Foundation.9*10* Thanks to Cliff Lawson for his help11*12* The Amstrad Delta keyboard (aka mailboard) uses normal PC-AT style serial13* transmission. The keyboard port is formed of two GPIO lines, for clock14* and data. Due to strict timing requirements of the interface,15* the serial data stream is read and processed by a FIQ handler.16* The resulting words are fetched by this driver from a circular buffer.17*18* Standard AT keyboard driver (atkbd) is used for handling the keyboard data.19* However, when used with the E3 mailboard that producecs non-standard20* scancodes, a custom key table must be prepared and loaded from userspace.21*/22#include <linux/gpio.h>23#include <linux/irq.h>24#include <linux/serio.h>25#include <linux/slab.h>2627#include <asm/mach-types.h>28#include <plat/board-ams-delta.h>2930#include <mach/ams-delta-fiq.h>3132MODULE_AUTHOR("Matt Callow");33MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver");34MODULE_LICENSE("GPL");3536static struct serio *ams_delta_serio;3738static int check_data(int data)39{40int i, parity = 0;4142/* check valid stop bit */43if (!(data & 0x400)) {44dev_warn(&ams_delta_serio->dev,45"invalid stop bit, data=0x%X\n",46data);47return SERIO_FRAME;48}49/* calculate the parity */50for (i = 1; i < 10; i++) {51if (data & (1 << i))52parity++;53}54/* it should be odd */55if (!(parity & 0x01)) {56dev_warn(&ams_delta_serio->dev,57"paritiy check failed, data=0x%X parity=0x%X\n",58data, parity);59return SERIO_PARITY;60}61return 0;62}6364static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)65{66int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF];67int data, dfl;68u8 scancode;6970fiq_buffer[FIQ_IRQ_PEND] = 0;7172/*73* Read data from the circular buffer, check it74* and then pass it on the serio75*/76while (fiq_buffer[FIQ_KEYS_CNT] > 0) {7778data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++];79fiq_buffer[FIQ_KEYS_CNT]--;80if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN])81fiq_buffer[FIQ_HEAD_OFFSET] = 0;8283dfl = check_data(data);84scancode = (u8) (data >> 1) & 0xFF;85serio_interrupt(ams_delta_serio, scancode, dfl);86}87return IRQ_HANDLED;88}8990static int ams_delta_serio_open(struct serio *serio)91{92/* enable keyboard */93ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR,94AMD_DELTA_LATCH2_KEYBRD_PWR);9596return 0;97}9899static void ams_delta_serio_close(struct serio *serio)100{101/* disable keyboard */102ams_delta_latch2_write(AMD_DELTA_LATCH2_KEYBRD_PWR, 0);103}104105static int __init ams_delta_serio_init(void)106{107int err;108109if (!machine_is_ams_delta())110return -ENODEV;111112ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);113if (!ams_delta_serio)114return -ENOMEM;115116ams_delta_serio->id.type = SERIO_8042;117ams_delta_serio->open = ams_delta_serio_open;118ams_delta_serio->close = ams_delta_serio_close;119strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter",120sizeof(ams_delta_serio->name));121strlcpy(ams_delta_serio->phys, "GPIO/serio0",122sizeof(ams_delta_serio->phys));123124err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_DATA, "serio-data");125if (err) {126pr_err("ams_delta_serio: Couldn't request gpio pin for data\n");127goto serio;128}129gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);130131err = gpio_request(AMS_DELTA_GPIO_PIN_KEYBRD_CLK, "serio-clock");132if (err) {133pr_err("ams_delta_serio: couldn't request gpio pin for clock\n");134goto gpio_data;135}136gpio_direction_input(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);137138err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),139ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,140"ams-delta-serio", 0);141if (err < 0) {142pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n",143gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));144goto gpio_clk;145}146/*147* Since GPIO register handling for keyboard clock pin is performed148* at FIQ level, switch back from edge to simple interrupt handler149* to avoid bad interaction.150*/151irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),152handle_simple_irq);153154serio_register_port(ams_delta_serio);155dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);156157return 0;158gpio_clk:159gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);160gpio_data:161gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);162serio:163kfree(ams_delta_serio);164return err;165}166module_init(ams_delta_serio_init);167168static void __exit ams_delta_serio_exit(void)169{170serio_unregister_port(ams_delta_serio);171free_irq(OMAP_GPIO_IRQ(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);172gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_CLK);173gpio_free(AMS_DELTA_GPIO_PIN_KEYBRD_DATA);174}175module_exit(ams_delta_serio_exit);176177178