Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
ams_delta_serio.c
Go to the documentation of this file.
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 #include <linux/module.h>
28 
29 #include <asm/mach-types.h>
30 #include <mach/board-ams-delta.h>
31 
32 #include <mach/ams-delta-fiq.h>
33 
34 MODULE_AUTHOR("Matt Callow");
35 MODULE_DESCRIPTION("AMS Delta (E3) keyboard port driver");
36 MODULE_LICENSE("GPL");
37 
38 static struct serio *ams_delta_serio;
39 
40 static int check_data(int data)
41 {
42  int i, parity = 0;
43 
44  /* check valid stop bit */
45  if (!(data & 0x400)) {
46  dev_warn(&ams_delta_serio->dev,
47  "invalid stop bit, data=0x%X\n",
48  data);
49  return SERIO_FRAME;
50  }
51  /* calculate the parity */
52  for (i = 1; i < 10; i++) {
53  if (data & (1 << i))
54  parity++;
55  }
56  /* it should be odd */
57  if (!(parity & 0x01)) {
58  dev_warn(&ams_delta_serio->dev,
59  "paritiy check failed, data=0x%X parity=0x%X\n",
60  data, parity);
61  return SERIO_PARITY;
62  }
63  return 0;
64 }
65 
66 static irqreturn_t ams_delta_serio_interrupt(int irq, void *dev_id)
67 {
68  int *circ_buff = &fiq_buffer[FIQ_CIRC_BUFF];
69  int data, dfl;
70  u8 scancode;
71 
73 
74  /*
75  * Read data from the circular buffer, check it
76  * and then pass it on the serio
77  */
78  while (fiq_buffer[FIQ_KEYS_CNT] > 0) {
79 
80  data = circ_buff[fiq_buffer[FIQ_HEAD_OFFSET]++];
81  fiq_buffer[FIQ_KEYS_CNT]--;
82  if (fiq_buffer[FIQ_HEAD_OFFSET] == fiq_buffer[FIQ_BUF_LEN])
83  fiq_buffer[FIQ_HEAD_OFFSET] = 0;
84 
85  dfl = check_data(data);
86  scancode = (u8) (data >> 1) & 0xFF;
87  serio_interrupt(ams_delta_serio, scancode, dfl);
88  }
89  return IRQ_HANDLED;
90 }
91 
92 static int ams_delta_serio_open(struct serio *serio)
93 {
94  /* enable keyboard */
95  gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 1);
96 
97  return 0;
98 }
99 
100 static void ams_delta_serio_close(struct serio *serio)
101 {
102  /* disable keyboard */
103  gpio_set_value(AMS_DELTA_GPIO_PIN_KEYBRD_PWR, 0);
104 }
105 
106 static const struct gpio ams_delta_gpios[] __initconst_or_module = {
107  {
108  .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATA,
109  .flags = GPIOF_DIR_IN,
110  .label = "serio-data",
111  },
112  {
113  .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_CLK,
114  .flags = GPIOF_DIR_IN,
115  .label = "serio-clock",
116  },
117  {
118  .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_PWR,
119  .flags = GPIOF_OUT_INIT_LOW,
120  .label = "serio-power",
121  },
122  {
123  .gpio = AMS_DELTA_GPIO_PIN_KEYBRD_DATAOUT,
124  .flags = GPIOF_OUT_INIT_LOW,
125  .label = "serio-dataout",
126  },
127 };
128 
129 static int __init ams_delta_serio_init(void)
130 {
131  int err;
132 
133  if (!machine_is_ams_delta())
134  return -ENODEV;
135 
136  ams_delta_serio = kzalloc(sizeof(struct serio), GFP_KERNEL);
137  if (!ams_delta_serio)
138  return -ENOMEM;
139 
140  ams_delta_serio->id.type = SERIO_8042;
141  ams_delta_serio->open = ams_delta_serio_open;
142  ams_delta_serio->close = ams_delta_serio_close;
143  strlcpy(ams_delta_serio->name, "AMS DELTA keyboard adapter",
144  sizeof(ams_delta_serio->name));
145  strlcpy(ams_delta_serio->phys, "GPIO/serio0",
146  sizeof(ams_delta_serio->phys));
147 
148  err = gpio_request_array(ams_delta_gpios,
149  ARRAY_SIZE(ams_delta_gpios));
150  if (err) {
151  pr_err("ams_delta_serio: Couldn't request gpio pins\n");
152  goto serio;
153  }
154 
155  err = request_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
156  ams_delta_serio_interrupt, IRQ_TYPE_EDGE_RISING,
157  "ams-delta-serio", 0);
158  if (err < 0) {
159  pr_err("ams_delta_serio: couldn't request gpio interrupt %d\n",
160  gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK));
161  goto gpio;
162  }
163  /*
164  * Since GPIO register handling for keyboard clock pin is performed
165  * at FIQ level, switch back from edge to simple interrupt handler
166  * to avoid bad interaction.
167  */
168  irq_set_handler(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK),
170 
171  serio_register_port(ams_delta_serio);
172  dev_info(&ams_delta_serio->dev, "%s\n", ams_delta_serio->name);
173 
174  return 0;
175 gpio:
176  gpio_free_array(ams_delta_gpios,
177  ARRAY_SIZE(ams_delta_gpios));
178 serio:
179  kfree(ams_delta_serio);
180  return err;
181 }
182 module_init(ams_delta_serio_init);
183 
184 static void __exit ams_delta_serio_exit(void)
185 {
186  serio_unregister_port(ams_delta_serio);
187  free_irq(gpio_to_irq(AMS_DELTA_GPIO_PIN_KEYBRD_CLK), 0);
188  gpio_free_array(ams_delta_gpios,
189  ARRAY_SIZE(ams_delta_gpios));
190 }
191 module_exit(ams_delta_serio_exit);