diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 13:50:00 -0400 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2010-05-21 13:50:00 -0400 |
commit | a6f039869ff87e0a8d621e31d14bbb120c1dfa93 (patch) | |
tree | c8975a8d02893633d03efe5435aa8b0635298a93 /drivers/input | |
parent | e0bc5d4a54938eedcde14005210e6c08aa9727e4 (diff) | |
parent | f6304f5804f228b6c2fea9e3dfac25c5b2db9b38 (diff) |
Merge branch 'omap-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6
* 'omap-for-linus' of git://git.kernel.org/pub/scm/linux/kernel/git/tmlind/linux-omap-2.6: (113 commits)
omap4: Add support for i2c init
omap: Fix i2c platform init code for omap4
OMAP2 clock: fix recursive spinlock attempt when CONFIG_CPU_FREQ=y
OMAP powerdomain, hwmod, omap_device: add some credits
OMAP4 powerdomain: Support LOWPOWERSTATECHANGE for powerdomains
OMAP3 clock: add support for setting the divider for sys_clkout2 using clk_set_rate
OMAP4 powerdomain: Fix pwrsts flags for ALWAYS ON domains
OMAP: timers: Fix clock source names for OMAP4
OMAP4 clock: Support clk_set_parent
OMAP4: PRCM: Add offset defines for all CM registers
OMAP4: PRCM: Add offset defines for all PRM registers
OMAP4: PRCM: Remove duplicate definition of base addresses
OMAP4: PRM: Remove MPU internal code name and apply PRCM naming convention
OMAP4: CM: Remove non-functional registers in ES1.0
OMAP: hwmod: Replace WARN by pr_warning for clockdomain check
OMAP: hwmod: Rename hwmod name for the MPU
OMAP: hwmod: Do not exit the iteration if one clock init failed
OMAP: hwmod: Replace WARN by pr_warning if clock lookup failed
OMAP: hwmod: Remove IS_ERR check with omap_clk_get_by_name return value
OMAP: hwmod: Fix wrong pointer iteration in oh->slaves
...
Diffstat (limited to 'drivers/input')
-rw-r--r-- | drivers/input/serio/Kconfig | 16 | ||||
-rw-r--r-- | drivers/input/serio/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/serio/ams_delta_serio.c | 177 |
3 files changed, 194 insertions, 0 deletions
diff --git a/drivers/input/serio/Kconfig b/drivers/input/serio/Kconfig index 7e319d65ec57..f34f1dbeb577 100644 --- a/drivers/input/serio/Kconfig +++ b/drivers/input/serio/Kconfig | |||
@@ -209,4 +209,20 @@ config SERIO_ALTERA_PS2 | |||
209 | To compile this driver as a module, choose M here: the | 209 | To compile this driver as a module, choose M here: the |
210 | module will be called altera_ps2. | 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 | endif | 228 | endif |
diff --git a/drivers/input/serio/Makefile b/drivers/input/serio/Makefile index bf945f789d05..84c80bf7185e 100644 --- a/drivers/input/serio/Makefile +++ b/drivers/input/serio/Makefile | |||
@@ -21,5 +21,6 @@ obj-$(CONFIG_SERIO_PCIPS2) += pcips2.o | |||
21 | obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o | 21 | obj-$(CONFIG_SERIO_MACEPS2) += maceps2.o |
22 | obj-$(CONFIG_SERIO_LIBPS2) += libps2.o | 22 | obj-$(CONFIG_SERIO_LIBPS2) += libps2.o |
23 | obj-$(CONFIG_SERIO_RAW) += serio_raw.o | 23 | obj-$(CONFIG_SERIO_RAW) += serio_raw.o |
24 | obj-$(CONFIG_SERIO_AMS_DELTA) += ams_delta_serio.o | ||
24 | obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o | 25 | obj-$(CONFIG_SERIO_XILINX_XPS_PS2) += xilinx_ps2.o |
25 | obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o | 26 | obj-$(CONFIG_SERIO_ALTERA_PS2) += altera_ps2.o |
diff --git a/drivers/input/serio/ams_delta_serio.c b/drivers/input/serio/ams_delta_serio.c new file mode 100644 index 000000000000..8f1770e1e08b --- /dev/null +++ b/drivers/input/serio/ams_delta_serio.c | |||
@@ -0,0 +1,177 @@ | |||
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); | ||