1 // SPDX-License-Identifier: GPL-2.0+
3 * (C) Copyright 2002 ELTEC Elektronik AG
7 /* i8042.c - Intel 8042 keyboard driver routines */
18 #include <linux/delay.h>
20 DECLARE_GLOBAL_DATA_PTR;
24 #define out8(p, v) outb(v, p)
27 QUIRK_DUP_POR = 1 << 0,
31 struct i8042_kbd_priv {
32 bool extended; /* true if an extended keycode is expected next */
33 int quirks; /* quirks that we support */
36 static unsigned char ext_key_map[] = {
37 0x1c, /* keypad enter */
38 0x1d, /* right control */
39 0x35, /* keypad slash */
40 0x37, /* print screen */
43 0x47, /* editpad home */
44 0x48, /* editpad up */
45 0x49, /* editpad pgup */
46 0x4b, /* editpad left */
47 0x4d, /* editpad right */
48 0x4f, /* editpad end */
49 0x50, /* editpad dn */
50 0x51, /* editpad pgdn */
51 0x52, /* editpad ins */
52 0x53, /* editpad del */
56 static int kbd_input_empty(void)
58 int kbd_timeout = KBD_TIMEOUT * 1000;
60 while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
63 return kbd_timeout != -1;
66 static int kbd_output_full(void)
68 int kbd_timeout = KBD_TIMEOUT * 1000;
70 while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
73 return kbd_timeout != -1;
77 * check_leds() - Check the keyboard LEDs and update them it needed
79 * @ret: Value to return
80 * @return value of @ret
82 static int i8042_kbd_update_leds(struct udevice *dev, int leds)
85 out8(I8042_DATA_REG, CMD_SET_KBD_LED);
87 out8(I8042_DATA_REG, leds & 0x7);
92 static int kbd_write(int reg, int value)
94 if (!kbd_input_empty())
101 static int kbd_read(int reg)
103 if (!kbd_output_full())
109 static int kbd_cmd_read(int cmd)
111 if (kbd_write(I8042_CMD_REG, cmd))
114 return kbd_read(I8042_DATA_REG);
117 static int kbd_cmd_write(int cmd, int data)
119 if (kbd_write(I8042_CMD_REG, cmd))
122 return kbd_write(I8042_DATA_REG, data);
125 static int kbd_reset(int quirk)
129 /* controller self test */
130 if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
134 if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
135 kbd_read(I8042_DATA_REG) != KBD_ACK ||
136 kbd_read(I8042_DATA_REG) != KBD_POR)
139 if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
140 kbd_read(I8042_DATA_REG) != KBD_ACK)
143 /* set AT translation and disable irq */
144 config = kbd_cmd_read(CMD_RD_CONFIG);
148 /* Sometimes get a second byte */
149 else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
150 config = kbd_cmd_read(CMD_RD_CONFIG);
152 config |= CONFIG_AT_TRANS;
153 config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
154 if (kbd_cmd_write(CMD_WR_CONFIG, config))
157 /* enable keyboard */
158 if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
164 debug("%s: Keyboard failure\n", __func__);
168 static int kbd_controller_present(void)
170 return in8(I8042_STS_REG) != 0xff;
173 /** Flush all buffer from keyboard controller to host*/
174 static void i8042_flush(void)
179 * The delay is to give the keyboard controller some time
180 * to fill the next byte.
183 timeout = 100; /* wait for no longer than 100us */
184 while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
189 /* Try to pull next byte if not timeout */
190 if (in8(I8042_STS_REG) & STATUS_OBF)
198 * Disables the keyboard so that key strokes no longer generate scancodes to
201 * @return 0 if ok, -1 if keyboard input was found while disabling
203 static int i8042_disable(void)
205 if (kbd_input_empty() == 0)
208 /* Disable keyboard */
209 out8(I8042_CMD_REG, CMD_KBD_DIS);
211 if (kbd_input_empty() == 0)
217 static int i8042_kbd_check(struct input_config *input)
219 struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
221 if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
224 bool release = false;
228 scan_code = in8(I8042_DATA_REG);
229 if (scan_code == 0xfa) {
231 } else if (scan_code == 0xe0) {
232 priv->extended = true;
235 if (scan_code & 0x80) {
239 if (priv->extended) {
240 priv->extended = false;
241 for (i = 0; ext_key_map[i]; i++) {
242 if (ext_key_map[i] == scan_code) {
243 scan_code = 0x60 + i;
252 input_add_keycode(input, scan_code, release);
257 /* i8042_kbd_init - reset keyboard and init state flags */
258 static int i8042_start(struct udevice *dev)
260 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
261 struct i8042_kbd_priv *priv = dev_get_priv(dev);
262 struct input_config *input = &uc_priv->input;
267 if (!kbd_controller_present()) {
268 debug("i8042 keyboard controller is not present\n");
272 /* Init keyboard device (default US layout) */
274 penv = env_get("keymap");
276 if (strncmp(penv, "de", 3) == 0)
280 for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
281 if (try >= KBD_RESET_TRIES)
285 ret = input_add_tables(input, keymap == KBD_GER);
289 i8042_kbd_update_leds(dev, NORMAL);
290 debug("%s: started\n", __func__);
295 static int i8042_kbd_remove(struct udevice *dev)
298 log_debug("i8042_disable() failed. fine, continue.\n");
305 * Set up the i8042 keyboard. This is called by the stdio device handler
307 * We want to do this init when the keyboard is actually used rather than
308 * at start-up, since keyboard input may not currently be selected.
310 * Once the keyboard starts there will be a period during which we must
311 * wait for the keyboard to init. We do this only when a key is first
312 * read - see kbd_wait_for_fifo_init().
314 * @return 0 if ok, -ve on error
316 static int i8042_kbd_probe(struct udevice *dev)
318 struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
319 struct i8042_kbd_priv *priv = dev_get_priv(dev);
320 struct stdio_dev *sdev = &uc_priv->sdev;
321 struct input_config *input = &uc_priv->input;
324 if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
325 "intel,duplicate-por"))
326 priv->quirks |= QUIRK_DUP_POR;
328 /* Register the device. i8042_start() will be called soon */
330 input->read_keys = i8042_kbd_check;
331 input_allow_repeats(input, true);
332 strcpy(sdev->name, "i8042-kbd");
333 ret = input_stdio_register(sdev);
335 debug("%s: input_stdio_register() failed\n", __func__);
338 debug("%s: ready\n", __func__);
343 static const struct keyboard_ops i8042_kbd_ops = {
344 .start = i8042_start,
345 .update_leds = i8042_kbd_update_leds,
348 static const struct udevice_id i8042_kbd_ids[] = {
349 { .compatible = "intel,i8042-keyboard" },
353 U_BOOT_DRIVER(i8042_kbd) = {
355 .id = UCLASS_KEYBOARD,
356 .of_match = i8042_kbd_ids,
357 .probe = i8042_kbd_probe,
358 .remove = i8042_kbd_remove,
359 .ops = &i8042_kbd_ops,
360 .priv_auto = sizeof(struct i8042_kbd_priv),