116b195c8SJean-Christophe PLAGNIOL-VILLARD /*
216b195c8SJean-Christophe PLAGNIOL-VILLARD * (C) Copyright 2002 ELTEC Elektronik AG
316b195c8SJean-Christophe PLAGNIOL-VILLARD * Frank Gottschling <fgottschling@eltec.de>
416b195c8SJean-Christophe PLAGNIOL-VILLARD *
51a459660SWolfgang Denk * SPDX-License-Identifier: GPL-2.0+
616b195c8SJean-Christophe PLAGNIOL-VILLARD */
716b195c8SJean-Christophe PLAGNIOL-VILLARD
816b195c8SJean-Christophe PLAGNIOL-VILLARD /* i8042.c - Intel 8042 keyboard driver routines */
916b195c8SJean-Christophe PLAGNIOL-VILLARD
1016b195c8SJean-Christophe PLAGNIOL-VILLARD #include <common.h>
11dcbf8257SSimon Glass #include <dm.h>
12dcbf8257SSimon Glass #include <errno.h>
1316b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h>
142ec739dbSSimon Glass #include <input.h>
15dcbf8257SSimon Glass #include <keyboard.h>
162ec739dbSSimon Glass #include <asm/io.h>
1716b195c8SJean-Christophe PLAGNIOL-VILLARD
18011d89d6SSimon Glass DECLARE_GLOBAL_DATA_PTR;
19011d89d6SSimon Glass
2016b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */
21835dd000SBin Meng #define in8(p) inb(p)
22835dd000SBin Meng #define out8(p, v) outb(v, p)
2316b195c8SJean-Christophe PLAGNIOL-VILLARD
24011d89d6SSimon Glass enum {
25011d89d6SSimon Glass QUIRK_DUP_POR = 1 << 0,
26011d89d6SSimon Glass };
27011d89d6SSimon Glass
2816b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */
29dcbf8257SSimon Glass struct i8042_kbd_priv {
30dcbf8257SSimon Glass bool extended; /* true if an extended keycode is expected next */
31011d89d6SSimon Glass int quirks; /* quirks that we support */
32dcbf8257SSimon Glass };
3316b195c8SJean-Christophe PLAGNIOL-VILLARD
34dd4a5b22SGabe Black static unsigned char ext_key_map[] = {
3516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1c, /* keypad enter */
3616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1d, /* right control */
3716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x35, /* keypad slash */
3816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x37, /* print screen */
3916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x38, /* right alt */
4016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x46, /* break */
4116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x47, /* editpad home */
4216b195c8SJean-Christophe PLAGNIOL-VILLARD 0x48, /* editpad up */
4316b195c8SJean-Christophe PLAGNIOL-VILLARD 0x49, /* editpad pgup */
4416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4b, /* editpad left */
4516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4d, /* editpad right */
4616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4f, /* editpad end */
4716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x50, /* editpad dn */
4816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x51, /* editpad pgdn */
4916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x52, /* editpad ins */
5016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x53, /* editpad del */
5116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x00 /* map end */
5216b195c8SJean-Christophe PLAGNIOL-VILLARD };
5316b195c8SJean-Christophe PLAGNIOL-VILLARD
kbd_input_empty(void)543928d66aSBin Meng static int kbd_input_empty(void)
553928d66aSBin Meng {
56835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000;
573928d66aSBin Meng
58835dd000SBin Meng while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
593928d66aSBin Meng udelay(1);
603928d66aSBin Meng
61835dd000SBin Meng return kbd_timeout != -1;
623928d66aSBin Meng }
633928d66aSBin Meng
kbd_output_full(void)64835dd000SBin Meng static int kbd_output_full(void)
653928d66aSBin Meng {
66835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000;
673928d66aSBin Meng
68835dd000SBin Meng while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
693928d66aSBin Meng udelay(1);
703928d66aSBin Meng
71835dd000SBin Meng return kbd_timeout != -1;
723928d66aSBin Meng }
733928d66aSBin Meng
74dcbf8257SSimon Glass /**
75dcbf8257SSimon Glass * check_leds() - Check the keyboard LEDs and update them it needed
76dcbf8257SSimon Glass *
77dcbf8257SSimon Glass * @ret: Value to return
78dcbf8257SSimon Glass * @return value of @ret
79dcbf8257SSimon Glass */
i8042_kbd_update_leds(struct udevice * dev,int leds)80dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds)
813928d66aSBin Meng {
823928d66aSBin Meng kbd_input_empty();
83835dd000SBin Meng out8(I8042_DATA_REG, CMD_SET_KBD_LED);
843928d66aSBin Meng kbd_input_empty();
85dcbf8257SSimon Glass out8(I8042_DATA_REG, leds & 0x7);
86dcbf8257SSimon Glass
87dcbf8257SSimon Glass return 0;
883928d66aSBin Meng }
893928d66aSBin Meng
kbd_write(int reg,int value)9031d38ee6SSimon Glass static int kbd_write(int reg, int value)
9131d38ee6SSimon Glass {
9231d38ee6SSimon Glass if (!kbd_input_empty())
9331d38ee6SSimon Glass return -1;
9431d38ee6SSimon Glass out8(reg, value);
9531d38ee6SSimon Glass
9631d38ee6SSimon Glass return 0;
9731d38ee6SSimon Glass }
9831d38ee6SSimon Glass
kbd_read(int reg)9931d38ee6SSimon Glass static int kbd_read(int reg)
10031d38ee6SSimon Glass {
10131d38ee6SSimon Glass if (!kbd_output_full())
10231d38ee6SSimon Glass return -1;
10331d38ee6SSimon Glass
10431d38ee6SSimon Glass return in8(reg);
10531d38ee6SSimon Glass }
10631d38ee6SSimon Glass
kbd_cmd_read(int cmd)10731d38ee6SSimon Glass static int kbd_cmd_read(int cmd)
10831d38ee6SSimon Glass {
10931d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd))
11031d38ee6SSimon Glass return -1;
11131d38ee6SSimon Glass
11231d38ee6SSimon Glass return kbd_read(I8042_DATA_REG);
11331d38ee6SSimon Glass }
11431d38ee6SSimon Glass
kbd_cmd_write(int cmd,int data)11531d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data)
11631d38ee6SSimon Glass {
11731d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd))
11831d38ee6SSimon Glass return -1;
11931d38ee6SSimon Glass
12031d38ee6SSimon Glass return kbd_write(I8042_DATA_REG, data);
12131d38ee6SSimon Glass }
12231d38ee6SSimon Glass
kbd_reset(int quirk)123011d89d6SSimon Glass static int kbd_reset(int quirk)
1243928d66aSBin Meng {
12531d38ee6SSimon Glass int config;
1267d96166bSBin Meng
1277d96166bSBin Meng /* controller self test */
12831d38ee6SSimon Glass if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
1294f087bacSSimon Glass goto err;
1303928d66aSBin Meng
1317d96166bSBin Meng /* keyboard reset */
13231d38ee6SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
13331d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK ||
13431d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_POR)
1354f087bacSSimon Glass goto err;
1363928d66aSBin Meng
1378226a3e9SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
1388226a3e9SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK)
1398226a3e9SSimon Glass goto err;
1408226a3e9SSimon Glass
1417d96166bSBin Meng /* set AT translation and disable irq */
14231d38ee6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG);
14331d38ee6SSimon Glass if (config == -1)
1444f087bacSSimon Glass goto err;
14531d38ee6SSimon Glass
146011d89d6SSimon Glass /* Sometimes get a second byte */
147011d89d6SSimon Glass else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
148011d89d6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG);
149011d89d6SSimon Glass
1507d96166bSBin Meng config |= CONFIG_AT_TRANS;
1517d96166bSBin Meng config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN);
15231d38ee6SSimon Glass if (kbd_cmd_write(CMD_WR_CONFIG, config))
1534f087bacSSimon Glass goto err;
1543928d66aSBin Meng
1557d96166bSBin Meng /* enable keyboard */
15631d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
15731d38ee6SSimon Glass !kbd_input_empty())
1584f087bacSSimon Glass goto err;
1593928d66aSBin Meng
1603928d66aSBin Meng return 0;
1614f087bacSSimon Glass err:
1624f087bacSSimon Glass debug("%s: Keyboard failure\n", __func__);
1634f087bacSSimon Glass return -1;
1643928d66aSBin Meng }
1653928d66aSBin Meng
kbd_controller_present(void)16622e0f5a9SGabe Black static int kbd_controller_present(void)
16722e0f5a9SGabe Black {
168835dd000SBin Meng return in8(I8042_STS_REG) != 0xff;
16922e0f5a9SGabe Black }
17022e0f5a9SGabe Black
17148edb304SGabe Black /*
17248edb304SGabe Black * Implement a weak default function for boards that optionally
17348edb304SGabe Black * need to skip the i8042 initialization.
174dcbf8257SSimon Glass *
175dcbf8257SSimon Glass * TODO(sjg@chromium.org): Use device tree for this?
17648edb304SGabe Black */
board_i8042_skip(void)17748edb304SGabe Black int __weak board_i8042_skip(void)
17848edb304SGabe Black {
17948edb304SGabe Black /* As default, don't skip */
18048edb304SGabe Black return 0;
18148edb304SGabe Black }
18248edb304SGabe Black
i8042_flush(void)18345fe668fSLouis Yung-Chieh Lo void i8042_flush(void)
18445fe668fSLouis Yung-Chieh Lo {
18545fe668fSLouis Yung-Chieh Lo int timeout;
18645fe668fSLouis Yung-Chieh Lo
18745fe668fSLouis Yung-Chieh Lo /*
188835dd000SBin Meng * The delay is to give the keyboard controller some time
189835dd000SBin Meng * to fill the next byte.
19045fe668fSLouis Yung-Chieh Lo */
19145fe668fSLouis Yung-Chieh Lo while (1) {
19245fe668fSLouis Yung-Chieh Lo timeout = 100; /* wait for no longer than 100us */
193835dd000SBin Meng while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
19445fe668fSLouis Yung-Chieh Lo udelay(1);
19545fe668fSLouis Yung-Chieh Lo timeout--;
19645fe668fSLouis Yung-Chieh Lo }
19745fe668fSLouis Yung-Chieh Lo
198835dd000SBin Meng /* Try to pull next byte if not timeout */
199835dd000SBin Meng if (in8(I8042_STS_REG) & STATUS_OBF)
20045fe668fSLouis Yung-Chieh Lo in8(I8042_DATA_REG);
20145fe668fSLouis Yung-Chieh Lo else
20245fe668fSLouis Yung-Chieh Lo break;
20345fe668fSLouis Yung-Chieh Lo }
20445fe668fSLouis Yung-Chieh Lo }
20545fe668fSLouis Yung-Chieh Lo
i8042_disable(void)20645fe668fSLouis Yung-Chieh Lo int i8042_disable(void)
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 /* Disable keyboard */
212835dd000SBin Meng out8(I8042_CMD_REG, CMD_KBD_DIS);
21345fe668fSLouis Yung-Chieh Lo
21445fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0)
21545fe668fSLouis Yung-Chieh Lo return -1;
21645fe668fSLouis Yung-Chieh Lo
21745fe668fSLouis Yung-Chieh Lo return 0;
21845fe668fSLouis Yung-Chieh Lo }
21945fe668fSLouis Yung-Chieh Lo
i8042_kbd_check(struct input_config * input)2202ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input)
2212ec739dbSSimon Glass {
222dcbf8257SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
223dcbf8257SSimon Glass
2242ec739dbSSimon Glass if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
2252ec739dbSSimon Glass return 0;
2262ec739dbSSimon Glass } else {
2272ec739dbSSimon Glass bool release = false;
2282ec739dbSSimon Glass int scan_code;
2292ec739dbSSimon Glass int i;
2302ec739dbSSimon Glass
2312ec739dbSSimon Glass scan_code = in8(I8042_DATA_REG);
2322ec739dbSSimon Glass if (scan_code == 0xfa) {
2332ec739dbSSimon Glass return 0;
2342ec739dbSSimon Glass } else if (scan_code == 0xe0) {
235dcbf8257SSimon Glass priv->extended = true;
2362ec739dbSSimon Glass return 0;
2372ec739dbSSimon Glass }
2382ec739dbSSimon Glass if (scan_code & 0x80) {
2392ec739dbSSimon Glass scan_code &= 0x7f;
2402ec739dbSSimon Glass release = true;
2412ec739dbSSimon Glass }
242dcbf8257SSimon Glass if (priv->extended) {
243dcbf8257SSimon Glass priv->extended = false;
2442ec739dbSSimon Glass for (i = 0; ext_key_map[i]; i++) {
2452ec739dbSSimon Glass if (ext_key_map[i] == scan_code) {
2462ec739dbSSimon Glass scan_code = 0x60 + i;
2472ec739dbSSimon Glass break;
2482ec739dbSSimon Glass }
2492ec739dbSSimon Glass }
2502ec739dbSSimon Glass /* not found ? */
2512ec739dbSSimon Glass if (!ext_key_map[i])
2522ec739dbSSimon Glass return 0;
2532ec739dbSSimon Glass }
2542ec739dbSSimon Glass
255dcbf8257SSimon Glass input_add_keycode(input, scan_code, release);
2562ec739dbSSimon Glass return 1;
2572ec739dbSSimon Glass }
2582ec739dbSSimon Glass }
2592ec739dbSSimon Glass
260835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */
i8042_start(struct udevice * dev)261dcbf8257SSimon Glass static int i8042_start(struct udevice *dev)
26216b195c8SJean-Christophe PLAGNIOL-VILLARD {
263dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
264011d89d6SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(dev);
265dcbf8257SSimon Glass struct input_config *input = &uc_priv->input;
26616b195c8SJean-Christophe PLAGNIOL-VILLARD int keymap, try;
26716b195c8SJean-Christophe PLAGNIOL-VILLARD char *penv;
2682ec739dbSSimon Glass int ret;
26916b195c8SJean-Christophe PLAGNIOL-VILLARD
270835dd000SBin Meng if (!kbd_controller_present() || board_i8042_skip()) {
271835dd000SBin Meng debug("i8042 keyboard controller is not present\n");
272dcbf8257SSimon Glass return -ENOENT;
273835dd000SBin Meng }
27422e0f5a9SGabe Black
27516b195c8SJean-Christophe PLAGNIOL-VILLARD /* Init keyboard device (default US layout) */
27616b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_US;
277*00caae6dSSimon Glass penv = env_get("keymap");
278dd4a5b22SGabe Black if (penv != NULL) {
27916b195c8SJean-Christophe PLAGNIOL-VILLARD if (strncmp(penv, "de", 3) == 0)
28016b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_GER;
28116b195c8SJean-Christophe PLAGNIOL-VILLARD }
28216b195c8SJean-Christophe PLAGNIOL-VILLARD
283011d89d6SSimon Glass for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
284c5d257f9SSimon Glass if (try >= KBD_RESET_TRIES)
285c5d257f9SSimon Glass return -1;
286c5d257f9SSimon Glass }
287c5d257f9SSimon Glass
288dcbf8257SSimon Glass ret = input_add_tables(input, keymap == KBD_GER);
2892ec739dbSSimon Glass if (ret)
2902ec739dbSSimon Glass return ret;
2912ec739dbSSimon Glass
292dcbf8257SSimon Glass i8042_kbd_update_leds(dev, NORMAL);
293dcbf8257SSimon Glass debug("%s: started\n", __func__);
294835dd000SBin Meng
29516b195c8SJean-Christophe PLAGNIOL-VILLARD return 0;
29616b195c8SJean-Christophe PLAGNIOL-VILLARD }
29716b195c8SJean-Christophe PLAGNIOL-VILLARD
2982ec739dbSSimon Glass /**
299dcbf8257SSimon Glass * Set up the i8042 keyboard. This is called by the stdio device handler
300835dd000SBin Meng *
301dcbf8257SSimon Glass * We want to do this init when the keyboard is actually used rather than
302dcbf8257SSimon Glass * at start-up, since keyboard input may not currently be selected.
303dcbf8257SSimon Glass *
304dcbf8257SSimon Glass * Once the keyboard starts there will be a period during which we must
305dcbf8257SSimon Glass * wait for the keyboard to init. We do this only when a key is first
306dcbf8257SSimon Glass * read - see kbd_wait_for_fifo_init().
307dcbf8257SSimon Glass *
308dcbf8257SSimon Glass * @return 0 if ok, -ve on error
30916b195c8SJean-Christophe PLAGNIOL-VILLARD */
i8042_kbd_probe(struct udevice * dev)310dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev)
3112ec739dbSSimon Glass {
312dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
313011d89d6SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(dev);
314dcbf8257SSimon Glass struct stdio_dev *sdev = &uc_priv->sdev;
315dcbf8257SSimon Glass struct input_config *input = &uc_priv->input;
316dcbf8257SSimon Glass int ret;
3172ec739dbSSimon Glass
318e160f7d4SSimon Glass if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
319011d89d6SSimon Glass "intel,duplicate-por"))
320011d89d6SSimon Glass priv->quirks |= QUIRK_DUP_POR;
321011d89d6SSimon Glass
322dcbf8257SSimon Glass /* Register the device. i8042_start() will be called soon */
323dcbf8257SSimon Glass input->dev = dev;
324dcbf8257SSimon Glass input->read_keys = i8042_kbd_check;
325dcbf8257SSimon Glass input_allow_repeats(input, true);
326dcbf8257SSimon Glass strcpy(sdev->name, "i8042-kbd");
327dcbf8257SSimon Glass ret = input_stdio_register(sdev);
328dcbf8257SSimon Glass if (ret) {
329dcbf8257SSimon Glass debug("%s: input_stdio_register() failed\n", __func__);
3302ec739dbSSimon Glass return ret;
3312ec739dbSSimon Glass }
332dcbf8257SSimon Glass debug("%s: ready\n", __func__);
3332ec739dbSSimon Glass
334dcbf8257SSimon Glass return 0;
33516b195c8SJean-Christophe PLAGNIOL-VILLARD }
336835dd000SBin Meng
337dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = {
338dcbf8257SSimon Glass .start = i8042_start,
339dcbf8257SSimon Glass .update_leds = i8042_kbd_update_leds,
340dcbf8257SSimon Glass };
341dcbf8257SSimon Glass
342dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = {
343dcbf8257SSimon Glass { .compatible = "intel,i8042-keyboard" },
344dcbf8257SSimon Glass { }
345dcbf8257SSimon Glass };
346dcbf8257SSimon Glass
347dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = {
348dcbf8257SSimon Glass .name = "i8042_kbd",
349dcbf8257SSimon Glass .id = UCLASS_KEYBOARD,
350dcbf8257SSimon Glass .of_match = i8042_kbd_ids,
351dcbf8257SSimon Glass .probe = i8042_kbd_probe,
352dcbf8257SSimon Glass .ops = &i8042_kbd_ops,
353dcbf8257SSimon Glass .priv_auto_alloc_size = sizeof(struct i8042_kbd_priv),
354dcbf8257SSimon Glass };
355