183d290c5STom Rini // SPDX-License-Identifier: GPL-2.0+
216b195c8SJean-Christophe PLAGNIOL-VILLARD /*
316b195c8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2002 ELTEC Elektronik AG
416b195c8SJean-Christophe PLAGNIOL-VILLARD * Frank Gottschling <fgottschling@eltec.de>
516b195c8SJean-Christophe PLAGNIOL-VILLARD */
616b195c8SJean-Christophe PLAGNIOL-VILLARD
716b195c8SJean-Christophe PLAGNIOL-VILLARD /* i8042.c - Intel 8042 keyboard driver routines */
816b195c8SJean-Christophe PLAGNIOL-VILLARD
916b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
10dcbf8257SSimon Glass #include <dm.h>
11dcbf8257SSimon Glass #include <errno.h>
1216b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
132ec739dbSSimon Glass #include <input.h>
14dcbf8257SSimon Glass #include <keyboard.h>
152ec739dbSSimon Glass #include <asm/io.h>
1616b195c8SJean-Christophe PLAGNIOL-VILLARD
17011d89d6SSimon Glass DECLARE_GLOBAL_DATA_PTR;
18011d89d6SSimon Glass
1916b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
20835dd000SBin Meng #define in8(p) inb(p)
21835dd000SBin Meng #define out8(p, v) outb(v, p)
2216b195c8SJean-Christophe PLAGNIOL-VILLARD
23011d89d6SSimon Glass enum {
24011d89d6SSimon Glass QUIRK_DUP_POR = 1 << 0,
25011d89d6SSimon Glass };
26011d89d6SSimon Glass
2716b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
28dcbf8257SSimon Glass struct i8042_kbd_priv {
29dcbf8257SSimon Glass bool extended; /* true if an extended keycode is expected next */
30011d89d6SSimon Glass int quirks; /* quirks that we support */
31dcbf8257SSimon Glass };
3216b195c8SJean-Christophe PLAGNIOL-VILLARD
33dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
3416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1c, /* keypad enter */
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1d, /* right control */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x35, /* keypad slash */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x37, /* print screen */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x38, /* right alt */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x46, /* break */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x47, /* editpad home */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x48, /* editpad up */
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 0x49, /* editpad pgup */
4316b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4b, /* editpad left */
4416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4d, /* editpad right */
4516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4f, /* editpad end */
4616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x50, /* editpad dn */
4716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x51, /* editpad pgdn */
4816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x52, /* editpad ins */
4916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x53, /* editpad del */
5016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x00 /* map end */
5116b195c8SJean-Christophe PLAGNIOL-VILLARD };
5216b195c8SJean-Christophe PLAGNIOL-VILLARD
kbd_input_empty(void)533928d66aSBin Meng static int kbd_input_empty(void)
543928d66aSBin Meng {
55835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000;
563928d66aSBin Meng
57835dd000SBin Meng while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
583928d66aSBin Meng udelay(1);
593928d66aSBin Meng
60835dd000SBin Meng return kbd_timeout != -1;
613928d66aSBin Meng }
623928d66aSBin Meng
kbd_output_full(void)63835dd000SBin Meng static int kbd_output_full(void)
643928d66aSBin Meng {
65835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000;
663928d66aSBin Meng
67835dd000SBin Meng while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
683928d66aSBin Meng udelay(1);
693928d66aSBin Meng
70835dd000SBin Meng return kbd_timeout != -1;
713928d66aSBin Meng }
723928d66aSBin Meng
73dcbf8257SSimon Glass /**
74dcbf8257SSimon Glass * check_leds() - Check the keyboard LEDs and update them it needed
75dcbf8257SSimon Glass *
76dcbf8257SSimon Glass * @ret: Value to return
77dcbf8257SSimon Glass * @return value of @ret
78dcbf8257SSimon Glass */
i8042_kbd_update_leds(struct udevice * dev,int leds)79dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds)
803928d66aSBin Meng {
813928d66aSBin Meng kbd_input_empty();
82835dd000SBin Meng out8(I8042_DATA_REG, CMD_SET_KBD_LED);
833928d66aSBin Meng kbd_input_empty();
84dcbf8257SSimon Glass out8(I8042_DATA_REG, leds & 0x7);
85dcbf8257SSimon Glass
86dcbf8257SSimon Glass return 0;
873928d66aSBin Meng }
883928d66aSBin Meng
kbd_write(int reg,int value)8931d38ee6SSimon Glass static int kbd_write(int reg, int value)
9031d38ee6SSimon Glass {
9131d38ee6SSimon Glass if (!kbd_input_empty())
9231d38ee6SSimon Glass return -1;
9331d38ee6SSimon Glass out8(reg, value);
9431d38ee6SSimon Glass
9531d38ee6SSimon Glass return 0;
9631d38ee6SSimon Glass }
9731d38ee6SSimon Glass
kbd_read(int reg)9831d38ee6SSimon Glass static int kbd_read(int reg)
9931d38ee6SSimon Glass {
10031d38ee6SSimon Glass if (!kbd_output_full())
10131d38ee6SSimon Glass return -1;
10231d38ee6SSimon Glass
10331d38ee6SSimon Glass return in8(reg);
10431d38ee6SSimon Glass }
10531d38ee6SSimon Glass
kbd_cmd_read(int cmd)10631d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
10731d38ee6SSimon Glass {
10831d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd))
10931d38ee6SSimon Glass return -1;
11031d38ee6SSimon Glass
11131d38ee6SSimon Glass return kbd_read(I8042_DATA_REG);
11231d38ee6SSimon Glass }
11331d38ee6SSimon Glass
kbd_cmd_write(int cmd,int data)11431d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
11531d38ee6SSimon Glass {
11631d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd))
11731d38ee6SSimon Glass return -1;
11831d38ee6SSimon Glass
11931d38ee6SSimon Glass return kbd_write(I8042_DATA_REG, data);
12031d38ee6SSimon Glass }
12131d38ee6SSimon Glass
kbd_reset(int quirk)122011d89d6SSimon Glass static int kbd_reset(int quirk)
1233928d66aSBin Meng {
12431d38ee6SSimon Glass int config;
1257d96166bSBin Meng
1267d96166bSBin Meng /* controller self test */
12731d38ee6SSimon Glass if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1284f087bacSSimon Glass goto err;
1293928d66aSBin Meng
1307d96166bSBin Meng /* keyboard reset */
13131d38ee6SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
13231d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK ||
13331d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_POR)
1344f087bacSSimon Glass goto err;
1353928d66aSBin Meng
1368226a3e9SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
1378226a3e9SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK)
1388226a3e9SSimon Glass goto err;
1398226a3e9SSimon Glass
1407d96166bSBin Meng /* set AT translation and disable irq */
14131d38ee6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG);
14231d38ee6SSimon Glass if (config == -1)
1434f087bacSSimon Glass goto err;
14431d38ee6SSimon Glass
145011d89d6SSimon Glass /* Sometimes get a second byte */
146011d89d6SSimon Glass else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
147011d89d6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG);
148011d89d6SSimon Glass
1497d96166bSBin Meng config |= CONFIG_AT_TRANS;
1507d96166bSBin Meng config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
15131d38ee6SSimon Glass if (kbd_cmd_write(CMD_WR_CONFIG, config))
1524f087bacSSimon Glass goto err;
1533928d66aSBin Meng
1547d96166bSBin Meng /* enable keyboard */
15531d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
15631d38ee6SSimon Glass !kbd_input_empty())
1574f087bacSSimon Glass goto err;
1583928d66aSBin Meng
1593928d66aSBin Meng return 0;
1604f087bacSSimon Glass err:
1614f087bacSSimon Glass debug("%s: Keyboard failure\n", __func__);
1624f087bacSSimon Glass return -1;
1633928d66aSBin Meng }
1643928d66aSBin Meng
kbd_controller_present(void)16522e0f5a9SGabe Black static int kbd_controller_present(void)
16622e0f5a9SGabe Black {
167835dd000SBin Meng return in8(I8042_STS_REG) != 0xff;
16822e0f5a9SGabe Black }
16922e0f5a9SGabe Black
170*165be50fSSimon Glass /** Flush all buffer from keyboard controller to host*/
i8042_flush(void)171*165be50fSSimon Glass static void i8042_flush(void)
17245fe668fSLouis Yung-Chieh Lo {
17345fe668fSLouis Yung-Chieh Lo int timeout;
17445fe668fSLouis Yung-Chieh Lo
17545fe668fSLouis Yung-Chieh Lo /*
176835dd000SBin Meng * The delay is to give the keyboard controller some time
177835dd000SBin Meng * to fill the next byte.
17845fe668fSLouis Yung-Chieh Lo */
17945fe668fSLouis Yung-Chieh Lo while (1) {
18045fe668fSLouis Yung-Chieh Lo timeout = 100; /* wait for no longer than 100us */
181835dd000SBin Meng while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
18245fe668fSLouis Yung-Chieh Lo udelay(1);
18345fe668fSLouis Yung-Chieh Lo timeout--;
18445fe668fSLouis Yung-Chieh Lo }
18545fe668fSLouis Yung-Chieh Lo
186835dd000SBin Meng /* Try to pull next byte if not timeout */
187835dd000SBin Meng if (in8(I8042_STS_REG) & STATUS_OBF)
18845fe668fSLouis Yung-Chieh Lo in8(I8042_DATA_REG);
18945fe668fSLouis Yung-Chieh Lo else
19045fe668fSLouis Yung-Chieh Lo break;
19145fe668fSLouis Yung-Chieh Lo }
19245fe668fSLouis Yung-Chieh Lo }
19345fe668fSLouis Yung-Chieh Lo
194*165be50fSSimon Glass /**
195*165be50fSSimon Glass * Disables the keyboard so that key strokes no longer generate scancodes to
196*165be50fSSimon Glass * the host.
197*165be50fSSimon Glass *
198*165be50fSSimon Glass * @return 0 if ok, -1 if keyboard input was found while disabling
199*165be50fSSimon Glass */
i8042_disable(void)200*165be50fSSimon Glass static int i8042_disable(void)
20145fe668fSLouis Yung-Chieh Lo {
20245fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0)
20345fe668fSLouis Yung-Chieh Lo return -1;
20445fe668fSLouis Yung-Chieh Lo
20545fe668fSLouis Yung-Chieh Lo /* Disable keyboard */
206835dd000SBin Meng out8(I8042_CMD_REG, CMD_KBD_DIS);
20745fe668fSLouis Yung-Chieh Lo
20845fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0)
20945fe668fSLouis Yung-Chieh Lo return -1;
21045fe668fSLouis Yung-Chieh Lo
21145fe668fSLouis Yung-Chieh Lo return 0;
21245fe668fSLouis Yung-Chieh Lo }
21345fe668fSLouis Yung-Chieh Lo
i8042_kbd_check(struct input_config * input)2142ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
2152ec739dbSSimon Glass {
216dcbf8257SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
217dcbf8257SSimon Glass
2182ec739dbSSimon Glass if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
2192ec739dbSSimon Glass return 0;
2202ec739dbSSimon Glass } else {
2212ec739dbSSimon Glass bool release = false;
2222ec739dbSSimon Glass int scan_code;
2232ec739dbSSimon Glass int i;
2242ec739dbSSimon Glass
2252ec739dbSSimon Glass scan_code = in8(I8042_DATA_REG);
2262ec739dbSSimon Glass if (scan_code == 0xfa) {
2272ec739dbSSimon Glass return 0;
2282ec739dbSSimon Glass } else if (scan_code == 0xe0) {
229dcbf8257SSimon Glass priv->extended = true;
2302ec739dbSSimon Glass return 0;
2312ec739dbSSimon Glass }
2322ec739dbSSimon Glass if (scan_code & 0x80) {
2332ec739dbSSimon Glass scan_code &= 0x7f;
2342ec739dbSSimon Glass release = true;
2352ec739dbSSimon Glass }
236dcbf8257SSimon Glass if (priv->extended) {
237dcbf8257SSimon Glass priv->extended = false;
2382ec739dbSSimon Glass for (i = 0; ext_key_map[i]; i++) {
2392ec739dbSSimon Glass if (ext_key_map[i] == scan_code) {
2402ec739dbSSimon Glass scan_code = 0x60 + i;
2412ec739dbSSimon Glass break;
2422ec739dbSSimon Glass }
2432ec739dbSSimon Glass }
2442ec739dbSSimon Glass /* not found ? */
2452ec739dbSSimon Glass if (!ext_key_map[i])
2462ec739dbSSimon Glass return 0;
2472ec739dbSSimon Glass }
2482ec739dbSSimon Glass
249dcbf8257SSimon Glass input_add_keycode(input, scan_code, release);
2502ec739dbSSimon Glass return 1;
2512ec739dbSSimon Glass }
2522ec739dbSSimon Glass }
2532ec739dbSSimon Glass
254835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
i8042_start(struct udevice * dev)255dcbf8257SSimon Glass static int i8042_start(struct udevice *dev)
25616b195c8SJean-Christophe PLAGNIOL-VILLARD {
257dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
258011d89d6SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(dev);
259dcbf8257SSimon Glass struct input_config *input = &uc_priv->input;
26016b195c8SJean-Christophe PLAGNIOL-VILLARD int keymap, try;
26116b195c8SJean-Christophe PLAGNIOL-VILLARD char *penv;
2622ec739dbSSimon Glass int ret;
26316b195c8SJean-Christophe PLAGNIOL-VILLARD
264*165be50fSSimon Glass if (!kbd_controller_present()) {
265835dd000SBin Meng debug("i8042 keyboard controller is not present\n");
266dcbf8257SSimon Glass return -ENOENT;
267835dd000SBin Meng }
26822e0f5a9SGabe Black
26916b195c8SJean-Christophe PLAGNIOL-VILLARD /* Init keyboard device (default US layout) */
27016b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_US;
27100caae6dSSimon Glass penv = env_get("keymap");
272dd4a5b22SGabe Black if (penv != NULL) {
27316b195c8SJean-Christophe PLAGNIOL-VILLARD if (strncmp(penv, "de", 3) == 0)
27416b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_GER;
27516b195c8SJean-Christophe PLAGNIOL-VILLARD }
27616b195c8SJean-Christophe PLAGNIOL-VILLARD
277011d89d6SSimon Glass for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
278c5d257f9SSimon Glass if (try >= KBD_RESET_TRIES)
279c5d257f9SSimon Glass return -1;
280c5d257f9SSimon Glass }
281c5d257f9SSimon Glass
282dcbf8257SSimon Glass ret = input_add_tables(input, keymap == KBD_GER);
2832ec739dbSSimon Glass if (ret)
2842ec739dbSSimon Glass return ret;
2852ec739dbSSimon Glass
286dcbf8257SSimon Glass i8042_kbd_update_leds(dev, NORMAL);
287dcbf8257SSimon Glass debug("%s: started\n", __func__);
288835dd000SBin Meng
28916b195c8SJean-Christophe PLAGNIOL-VILLARD return 0;
29016b195c8SJean-Christophe PLAGNIOL-VILLARD }
29116b195c8SJean-Christophe PLAGNIOL-VILLARD
i8042_kbd_remove(struct udevice * dev)292*165be50fSSimon Glass static int i8042_kbd_remove(struct udevice *dev)
293*165be50fSSimon Glass {
294*165be50fSSimon Glass if (i8042_disable())
295*165be50fSSimon Glass log_debug("i8042_disable() failed. fine, continue.\n");
296*165be50fSSimon Glass i8042_flush();
297*165be50fSSimon Glass
298*165be50fSSimon Glass return 0;
299*165be50fSSimon Glass }
300*165be50fSSimon Glass
3012ec739dbSSimon Glass /**
302dcbf8257SSimon Glass * Set up the i8042 keyboard. This is called by the stdio device handler
303835dd000SBin Meng *
304dcbf8257SSimon Glass * We want to do this init when the keyboard is actually used rather than
305dcbf8257SSimon Glass * at start-up, since keyboard input may not currently be selected.
306dcbf8257SSimon Glass *
307dcbf8257SSimon Glass * Once the keyboard starts there will be a period during which we must
308dcbf8257SSimon Glass * wait for the keyboard to init. We do this only when a key is first
309dcbf8257SSimon Glass * read - see kbd_wait_for_fifo_init().
310dcbf8257SSimon Glass *
311dcbf8257SSimon Glass * @return 0 if ok, -ve on error
31216b195c8SJean-Christophe PLAGNIOL-VILLARD */
i8042_kbd_probe(struct udevice * dev)313dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev)
3142ec739dbSSimon Glass {
315dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
316011d89d6SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(dev);
317dcbf8257SSimon Glass struct stdio_dev *sdev = &uc_priv->sdev;
318dcbf8257SSimon Glass struct input_config *input = &uc_priv->input;
319dcbf8257SSimon Glass int ret;
3202ec739dbSSimon Glass
321e160f7d4SSimon Glass if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
322011d89d6SSimon Glass "intel,duplicate-por"))
323011d89d6SSimon Glass priv->quirks |= QUIRK_DUP_POR;
324011d89d6SSimon Glass
325dcbf8257SSimon Glass /* Register the device. i8042_start() will be called soon */
326dcbf8257SSimon Glass input->dev = dev;
327dcbf8257SSimon Glass input->read_keys = i8042_kbd_check;
328dcbf8257SSimon Glass input_allow_repeats(input, true);
329dcbf8257SSimon Glass strcpy(sdev->name, "i8042-kbd");
330dcbf8257SSimon Glass ret = input_stdio_register(sdev);
331dcbf8257SSimon Glass if (ret) {
332dcbf8257SSimon Glass debug("%s: input_stdio_register() failed\n", __func__);
3332ec739dbSSimon Glass return ret;
3342ec739dbSSimon Glass }
335dcbf8257SSimon Glass debug("%s: ready\n", __func__);
3362ec739dbSSimon Glass
337dcbf8257SSimon Glass return 0;
33816b195c8SJean-Christophe PLAGNIOL-VILLARD }
339835dd000SBin Meng
340dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = {
341dcbf8257SSimon Glass .start = i8042_start,
342dcbf8257SSimon Glass .update_leds = i8042_kbd_update_leds,
343dcbf8257SSimon Glass };
344dcbf8257SSimon Glass
345dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = {
346dcbf8257SSimon Glass { .compatible = "intel,i8042-keyboard" },
347dcbf8257SSimon Glass { }
348dcbf8257SSimon Glass };
349dcbf8257SSimon Glass
350dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = {
351dcbf8257SSimon Glass .name = "i8042_kbd",
352dcbf8257SSimon Glass .id = UCLASS_KEYBOARD,
353dcbf8257SSimon Glass .of_match = i8042_kbd_ids,
354dcbf8257SSimon Glass .probe = i8042_kbd_probe,
355*165be50fSSimon Glass .remove = i8042_kbd_remove,
356dcbf8257SSimon Glass .ops = &i8042_kbd_ops,
357dcbf8257SSimon Glass .priv_auto_alloc_size = sizeof(struct i8042_kbd_priv),
358dcbf8257SSimon Glass };
359