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 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 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 */ 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 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 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 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 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 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 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 */ 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 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 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 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 */ 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; 277dd4a5b22SGabe Black penv = getenv("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 */ 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 318*e160f7d4SSimon 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