1
2
3
4
5
6
7
8
9#include <common.h>
10#include <dm.h>
11#include <env.h>
12#include <errno.h>
13#include <i8042.h>
14#include <input.h>
15#include <keyboard.h>
16#include <log.h>
17#include <asm/global_data.h>
18#include <asm/io.h>
19#include <linux/delay.h>
20
21DECLARE_GLOBAL_DATA_PTR;
22
23
24#define in8(p) inb(p)
25#define out8(p, v) outb(v, p)
26
27enum {
28 QUIRK_DUP_POR = 1 << 0,
29};
30
31
32struct i8042_kbd_priv {
33 bool extended;
34 int quirks;
35};
36
37static unsigned char ext_key_map[] = {
38 0x1c,
39 0x1d,
40 0x35,
41 0x37,
42 0x38,
43 0x46,
44 0x47,
45 0x48,
46 0x49,
47 0x4b,
48 0x4d,
49 0x4f,
50 0x50,
51 0x51,
52 0x52,
53 0x53,
54 0x00
55 };
56
57static int kbd_input_empty(void)
58{
59 int kbd_timeout = KBD_TIMEOUT * 1000;
60
61 while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
62 udelay(1);
63
64 return kbd_timeout != -1;
65}
66
67static int kbd_output_full(void)
68{
69 int kbd_timeout = KBD_TIMEOUT * 1000;
70
71 while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
72 udelay(1);
73
74 return kbd_timeout != -1;
75}
76
77
78
79
80
81
82
83static int i8042_kbd_update_leds(struct udevice *dev, int leds)
84{
85 kbd_input_empty();
86 out8(I8042_DATA_REG, CMD_SET_KBD_LED);
87 kbd_input_empty();
88 out8(I8042_DATA_REG, leds & 0x7);
89
90 return 0;
91}
92
93static int kbd_write(int reg, int value)
94{
95 if (!kbd_input_empty())
96 return -1;
97 out8(reg, value);
98
99 return 0;
100}
101
102static int kbd_read(int reg)
103{
104 if (!kbd_output_full())
105 return -1;
106
107 return in8(reg);
108}
109
110static int kbd_cmd_read(int cmd)
111{
112 if (kbd_write(I8042_CMD_REG, cmd))
113 return -1;
114
115 return kbd_read(I8042_DATA_REG);
116}
117
118static int kbd_cmd_write(int cmd, int data)
119{
120 if (kbd_write(I8042_CMD_REG, cmd))
121 return -1;
122
123 return kbd_write(I8042_DATA_REG, data);
124}
125
126static int kbd_reset(int quirk)
127{
128 int config;
129
130
131 if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
132 goto err;
133
134
135 if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
136 kbd_read(I8042_DATA_REG) != KBD_ACK ||
137 kbd_read(I8042_DATA_REG) != KBD_POR)
138 goto err;
139
140 if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
141 kbd_read(I8042_DATA_REG) != KBD_ACK)
142 goto err;
143
144
145 config = kbd_cmd_read(CMD_RD_CONFIG);
146 if (config == -1)
147 goto err;
148
149
150 else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
151 config = kbd_cmd_read(CMD_RD_CONFIG);
152
153 config |= CFG_AT_TRANS;
154 config &= ~(CFG_KIRQ_EN | CFG_MIRQ_EN);
155 if (kbd_cmd_write(CMD_WR_CONFIG, config))
156 goto err;
157
158
159 if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
160 !kbd_input_empty())
161 goto err;
162
163 return 0;
164err:
165 debug("%s: Keyboard failure\n", __func__);
166 return -1;
167}
168
169static int kbd_controller_present(void)
170{
171 return in8(I8042_STS_REG) != 0xff;
172}
173
174
175static void i8042_flush(void)
176{
177 int timeout;
178
179
180
181
182
183 while (1) {
184 timeout = 100;
185 while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
186 udelay(1);
187 timeout--;
188 }
189
190
191 if (in8(I8042_STS_REG) & STATUS_OBF)
192 in8(I8042_DATA_REG);
193 else
194 break;
195 }
196}
197
198
199
200
201
202
203
204static int i8042_disable(void)
205{
206 if (kbd_input_empty() == 0)
207 return -1;
208
209
210 out8(I8042_CMD_REG, CMD_KBD_DIS);
211
212 if (kbd_input_empty() == 0)
213 return -1;
214
215 return 0;
216}
217
218static int i8042_kbd_check(struct input_config *input)
219{
220 struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
221
222 if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
223 return 0;
224 } else {
225 bool release = false;
226 int scan_code;
227 int i;
228
229 scan_code = in8(I8042_DATA_REG);
230 if (scan_code == 0xfa) {
231 return 0;
232 } else if (scan_code == 0xe0) {
233 priv->extended = true;
234 return 0;
235 }
236 if (scan_code & 0x80) {
237 scan_code &= 0x7f;
238 release = true;
239 }
240 if (priv->extended) {
241 priv->extended = false;
242 for (i = 0; ext_key_map[i]; i++) {
243 if (ext_key_map[i] == scan_code) {
244 scan_code = 0x60 + i;
245 break;
246 }
247 }
248
249 if (!ext_key_map[i])
250 return 0;
251 }
252
253 input_add_keycode(input, scan_code, release);
254 return 1;
255 }
256}
257
258
259static int i8042_start(struct udevice *dev)
260{
261 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
262 struct i8042_kbd_priv *priv = dev_get_priv(dev);
263 struct input_config *input = &uc_priv->input;
264 int keymap, try;
265 char *penv;
266 int ret;
267
268 if (!kbd_controller_present()) {
269 debug("i8042 keyboard controller is not present\n");
270 return -ENOENT;
271 }
272
273
274 keymap = KBD_US;
275 penv = env_get("keymap");
276 if (penv != NULL) {
277 if (strncmp(penv, "de", 3) == 0)
278 keymap = KBD_GER;
279 }
280
281 for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
282 if (try >= KBD_RESET_TRIES)
283 return -1;
284 }
285
286 ret = input_add_tables(input, keymap == KBD_GER);
287 if (ret)
288 return ret;
289
290 i8042_kbd_update_leds(dev, NORMAL);
291 debug("%s: started\n", __func__);
292
293 return 0;
294}
295
296static int i8042_kbd_remove(struct udevice *dev)
297{
298 if (i8042_disable())
299 log_debug("i8042_disable() failed. fine, continue.\n");
300 i8042_flush();
301
302 return 0;
303}
304
305
306
307
308
309
310
311
312
313
314
315
316
317static int i8042_kbd_probe(struct udevice *dev)
318{
319 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
320 struct i8042_kbd_priv *priv = dev_get_priv(dev);
321 struct stdio_dev *sdev = &uc_priv->sdev;
322 struct input_config *input = &uc_priv->input;
323 int ret;
324
325 if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
326 "intel,duplicate-por"))
327 priv->quirks |= QUIRK_DUP_POR;
328
329
330 input->dev = dev;
331 input->read_keys = i8042_kbd_check;
332 input_allow_repeats(input, true);
333 strcpy(sdev->name, "i8042-kbd");
334 ret = input_stdio_register(sdev);
335 if (ret) {
336 debug("%s: input_stdio_register() failed\n", __func__);
337 return ret;
338 }
339 debug("%s: ready\n", __func__);
340
341 return 0;
342}
343
344static const struct keyboard_ops i8042_kbd_ops = {
345 .start = i8042_start,
346 .update_leds = i8042_kbd_update_leds,
347};
348
349static const struct udevice_id i8042_kbd_ids[] = {
350 { .compatible = "intel,i8042-keyboard" },
351 { }
352};
353
354U_BOOT_DRIVER(i8042_kbd) = {
355 .name = "i8042_kbd",
356 .id = UCLASS_KEYBOARD,
357 .of_match = i8042_kbd_ids,
358 .probe = i8042_kbd_probe,
359 .remove = i8042_kbd_remove,
360 .ops = &i8042_kbd_ops,
361 .priv_auto = sizeof(struct i8042_kbd_priv),
362};
363