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> 11*dcbf8257SSimon Glass #include <dm.h> 12*dcbf8257SSimon Glass #include <errno.h> 1316b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h> 142ec739dbSSimon Glass #include <input.h> 15*dcbf8257SSimon Glass #include <keyboard.h> 162ec739dbSSimon Glass #include <asm/io.h> 1716b195c8SJean-Christophe PLAGNIOL-VILLARD 1816b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */ 19835dd000SBin Meng #define in8(p) inb(p) 20835dd000SBin Meng #define out8(p, v) outb(v, p) 2116b195c8SJean-Christophe PLAGNIOL-VILLARD 2216b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */ 23*dcbf8257SSimon Glass struct i8042_kbd_priv { 24*dcbf8257SSimon Glass bool extended; /* true if an extended keycode is expected next */ 25*dcbf8257SSimon Glass }; 2616b195c8SJean-Christophe PLAGNIOL-VILLARD 27dd4a5b22SGabe Black static unsigned char ext_key_map[] = { 2816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1c, /* keypad enter */ 2916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1d, /* right control */ 3016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x35, /* keypad slash */ 3116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x37, /* print screen */ 3216b195c8SJean-Christophe PLAGNIOL-VILLARD 0x38, /* right alt */ 3316b195c8SJean-Christophe PLAGNIOL-VILLARD 0x46, /* break */ 3416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x47, /* editpad home */ 3516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x48, /* editpad up */ 3616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x49, /* editpad pgup */ 3716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4b, /* editpad left */ 3816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4d, /* editpad right */ 3916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4f, /* editpad end */ 4016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x50, /* editpad dn */ 4116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x51, /* editpad pgdn */ 4216b195c8SJean-Christophe PLAGNIOL-VILLARD 0x52, /* editpad ins */ 4316b195c8SJean-Christophe PLAGNIOL-VILLARD 0x53, /* editpad del */ 4416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x00 /* map end */ 4516b195c8SJean-Christophe PLAGNIOL-VILLARD }; 4616b195c8SJean-Christophe PLAGNIOL-VILLARD 473928d66aSBin Meng static int kbd_input_empty(void) 483928d66aSBin Meng { 49835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000; 503928d66aSBin Meng 51835dd000SBin Meng while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--) 523928d66aSBin Meng udelay(1); 533928d66aSBin Meng 54835dd000SBin Meng return kbd_timeout != -1; 553928d66aSBin Meng } 563928d66aSBin Meng 57835dd000SBin Meng static int kbd_output_full(void) 583928d66aSBin Meng { 59835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000; 603928d66aSBin Meng 61835dd000SBin Meng while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--) 623928d66aSBin Meng udelay(1); 633928d66aSBin Meng 64835dd000SBin Meng return kbd_timeout != -1; 653928d66aSBin Meng } 663928d66aSBin Meng 67*dcbf8257SSimon Glass /** 68*dcbf8257SSimon Glass * check_leds() - Check the keyboard LEDs and update them it needed 69*dcbf8257SSimon Glass * 70*dcbf8257SSimon Glass * @ret: Value to return 71*dcbf8257SSimon Glass * @return value of @ret 72*dcbf8257SSimon Glass */ 73*dcbf8257SSimon Glass static int i8042_kbd_update_leds(struct udevice *dev, int leds) 743928d66aSBin Meng { 753928d66aSBin Meng kbd_input_empty(); 76835dd000SBin Meng out8(I8042_DATA_REG, CMD_SET_KBD_LED); 773928d66aSBin Meng kbd_input_empty(); 78*dcbf8257SSimon Glass out8(I8042_DATA_REG, leds & 0x7); 79*dcbf8257SSimon Glass 80*dcbf8257SSimon Glass return 0; 813928d66aSBin Meng } 823928d66aSBin Meng 8331d38ee6SSimon Glass static int kbd_write(int reg, int value) 8431d38ee6SSimon Glass { 8531d38ee6SSimon Glass if (!kbd_input_empty()) 8631d38ee6SSimon Glass return -1; 8731d38ee6SSimon Glass out8(reg, value); 8831d38ee6SSimon Glass 8931d38ee6SSimon Glass return 0; 9031d38ee6SSimon Glass } 9131d38ee6SSimon Glass 9231d38ee6SSimon Glass static int kbd_read(int reg) 9331d38ee6SSimon Glass { 9431d38ee6SSimon Glass if (!kbd_output_full()) 9531d38ee6SSimon Glass return -1; 9631d38ee6SSimon Glass 9731d38ee6SSimon Glass return in8(reg); 9831d38ee6SSimon Glass } 9931d38ee6SSimon Glass 10031d38ee6SSimon Glass static int kbd_cmd_read(int cmd) 10131d38ee6SSimon Glass { 10231d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd)) 10331d38ee6SSimon Glass return -1; 10431d38ee6SSimon Glass 10531d38ee6SSimon Glass return kbd_read(I8042_DATA_REG); 10631d38ee6SSimon Glass } 10731d38ee6SSimon Glass 10831d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data) 10931d38ee6SSimon Glass { 11031d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd)) 11131d38ee6SSimon Glass return -1; 11231d38ee6SSimon Glass 11331d38ee6SSimon Glass return kbd_write(I8042_DATA_REG, data); 11431d38ee6SSimon Glass } 11531d38ee6SSimon Glass 1163928d66aSBin Meng static int kbd_reset(void) 1173928d66aSBin Meng { 11831d38ee6SSimon Glass int config; 1197d96166bSBin Meng 1207d96166bSBin Meng /* controller self test */ 12131d38ee6SSimon Glass if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK) 1224f087bacSSimon Glass goto err; 1233928d66aSBin Meng 1247d96166bSBin Meng /* keyboard reset */ 12531d38ee6SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) || 12631d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK || 12731d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_POR) 1284f087bacSSimon Glass goto err; 1293928d66aSBin Meng 1307d96166bSBin Meng /* set AT translation and disable irq */ 13131d38ee6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG); 13231d38ee6SSimon Glass if (config == -1) 1334f087bacSSimon Glass goto err; 13431d38ee6SSimon Glass 1357d96166bSBin Meng config |= CONFIG_AT_TRANS; 1367d96166bSBin Meng config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN); 13731d38ee6SSimon Glass if (kbd_cmd_write(CMD_WR_CONFIG, config)) 1384f087bacSSimon Glass goto err; 1393928d66aSBin Meng 1407d96166bSBin Meng /* enable keyboard */ 14131d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) || 14231d38ee6SSimon Glass !kbd_input_empty()) 1434f087bacSSimon Glass goto err; 1443928d66aSBin Meng 1453928d66aSBin Meng return 0; 1464f087bacSSimon Glass err: 1474f087bacSSimon Glass debug("%s: Keyboard failure\n", __func__); 1484f087bacSSimon Glass return -1; 1493928d66aSBin Meng } 1503928d66aSBin Meng 15122e0f5a9SGabe Black static int kbd_controller_present(void) 15222e0f5a9SGabe Black { 153835dd000SBin Meng return in8(I8042_STS_REG) != 0xff; 15422e0f5a9SGabe Black } 15522e0f5a9SGabe Black 15648edb304SGabe Black /* 15748edb304SGabe Black * Implement a weak default function for boards that optionally 15848edb304SGabe Black * need to skip the i8042 initialization. 159*dcbf8257SSimon Glass * 160*dcbf8257SSimon Glass * TODO(sjg@chromium.org): Use device tree for this? 16148edb304SGabe Black */ 16248edb304SGabe Black int __weak board_i8042_skip(void) 16348edb304SGabe Black { 16448edb304SGabe Black /* As default, don't skip */ 16548edb304SGabe Black return 0; 16648edb304SGabe Black } 16748edb304SGabe Black 16845fe668fSLouis Yung-Chieh Lo void i8042_flush(void) 16945fe668fSLouis Yung-Chieh Lo { 17045fe668fSLouis Yung-Chieh Lo int timeout; 17145fe668fSLouis Yung-Chieh Lo 17245fe668fSLouis Yung-Chieh Lo /* 173835dd000SBin Meng * The delay is to give the keyboard controller some time 174835dd000SBin Meng * to fill the next byte. 17545fe668fSLouis Yung-Chieh Lo */ 17645fe668fSLouis Yung-Chieh Lo while (1) { 17745fe668fSLouis Yung-Chieh Lo timeout = 100; /* wait for no longer than 100us */ 178835dd000SBin Meng while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) { 17945fe668fSLouis Yung-Chieh Lo udelay(1); 18045fe668fSLouis Yung-Chieh Lo timeout--; 18145fe668fSLouis Yung-Chieh Lo } 18245fe668fSLouis Yung-Chieh Lo 183835dd000SBin Meng /* Try to pull next byte if not timeout */ 184835dd000SBin Meng if (in8(I8042_STS_REG) & STATUS_OBF) 18545fe668fSLouis Yung-Chieh Lo in8(I8042_DATA_REG); 18645fe668fSLouis Yung-Chieh Lo else 18745fe668fSLouis Yung-Chieh Lo break; 18845fe668fSLouis Yung-Chieh Lo } 18945fe668fSLouis Yung-Chieh Lo } 19045fe668fSLouis Yung-Chieh Lo 19145fe668fSLouis Yung-Chieh Lo int i8042_disable(void) 19245fe668fSLouis Yung-Chieh Lo { 19345fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0) 19445fe668fSLouis Yung-Chieh Lo return -1; 19545fe668fSLouis Yung-Chieh Lo 19645fe668fSLouis Yung-Chieh Lo /* Disable keyboard */ 197835dd000SBin Meng out8(I8042_CMD_REG, CMD_KBD_DIS); 19845fe668fSLouis Yung-Chieh Lo 19945fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0) 20045fe668fSLouis Yung-Chieh Lo return -1; 20145fe668fSLouis Yung-Chieh Lo 20245fe668fSLouis Yung-Chieh Lo return 0; 20345fe668fSLouis Yung-Chieh Lo } 20445fe668fSLouis Yung-Chieh Lo 2052ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input) 2062ec739dbSSimon Glass { 207*dcbf8257SSimon Glass struct i8042_kbd_priv *priv = dev_get_priv(input->dev); 208*dcbf8257SSimon Glass 2092ec739dbSSimon Glass if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) { 2102ec739dbSSimon Glass return 0; 2112ec739dbSSimon Glass } else { 2122ec739dbSSimon Glass bool release = false; 2132ec739dbSSimon Glass int scan_code; 2142ec739dbSSimon Glass int i; 2152ec739dbSSimon Glass 2162ec739dbSSimon Glass scan_code = in8(I8042_DATA_REG); 2172ec739dbSSimon Glass if (scan_code == 0xfa) { 2182ec739dbSSimon Glass return 0; 2192ec739dbSSimon Glass } else if (scan_code == 0xe0) { 220*dcbf8257SSimon Glass priv->extended = true; 2212ec739dbSSimon Glass return 0; 2222ec739dbSSimon Glass } 2232ec739dbSSimon Glass if (scan_code & 0x80) { 2242ec739dbSSimon Glass scan_code &= 0x7f; 2252ec739dbSSimon Glass release = true; 2262ec739dbSSimon Glass } 227*dcbf8257SSimon Glass if (priv->extended) { 228*dcbf8257SSimon Glass priv->extended = false; 2292ec739dbSSimon Glass for (i = 0; ext_key_map[i]; i++) { 2302ec739dbSSimon Glass if (ext_key_map[i] == scan_code) { 2312ec739dbSSimon Glass scan_code = 0x60 + i; 2322ec739dbSSimon Glass break; 2332ec739dbSSimon Glass } 2342ec739dbSSimon Glass } 2352ec739dbSSimon Glass /* not found ? */ 2362ec739dbSSimon Glass if (!ext_key_map[i]) 2372ec739dbSSimon Glass return 0; 2382ec739dbSSimon Glass } 2392ec739dbSSimon Glass 240*dcbf8257SSimon Glass input_add_keycode(input, scan_code, release); 2412ec739dbSSimon Glass return 1; 2422ec739dbSSimon Glass } 2432ec739dbSSimon Glass } 2442ec739dbSSimon Glass 245835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */ 246*dcbf8257SSimon Glass static int i8042_start(struct udevice *dev) 24716b195c8SJean-Christophe PLAGNIOL-VILLARD { 248*dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); 249*dcbf8257SSimon Glass struct input_config *input = &uc_priv->input; 25016b195c8SJean-Christophe PLAGNIOL-VILLARD int keymap, try; 25116b195c8SJean-Christophe PLAGNIOL-VILLARD char *penv; 2522ec739dbSSimon Glass int ret; 25316b195c8SJean-Christophe PLAGNIOL-VILLARD 254835dd000SBin Meng if (!kbd_controller_present() || board_i8042_skip()) { 255835dd000SBin Meng debug("i8042 keyboard controller is not present\n"); 256*dcbf8257SSimon Glass return -ENOENT; 257835dd000SBin Meng } 25822e0f5a9SGabe Black 25916b195c8SJean-Christophe PLAGNIOL-VILLARD /* Init keyboard device (default US layout) */ 26016b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_US; 261dd4a5b22SGabe Black penv = getenv("keymap"); 262dd4a5b22SGabe Black if (penv != NULL) { 26316b195c8SJean-Christophe PLAGNIOL-VILLARD if (strncmp(penv, "de", 3) == 0) 26416b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_GER; 26516b195c8SJean-Christophe PLAGNIOL-VILLARD } 26616b195c8SJean-Christophe PLAGNIOL-VILLARD 267c5d257f9SSimon Glass for (try = 0; kbd_reset() != 0; try++) { 268c5d257f9SSimon Glass if (try >= KBD_RESET_TRIES) 269c5d257f9SSimon Glass return -1; 270c5d257f9SSimon Glass } 271c5d257f9SSimon Glass 272*dcbf8257SSimon Glass ret = input_add_tables(input, keymap == KBD_GER); 2732ec739dbSSimon Glass if (ret) 2742ec739dbSSimon Glass return ret; 2752ec739dbSSimon Glass 276*dcbf8257SSimon Glass i8042_kbd_update_leds(dev, NORMAL); 277*dcbf8257SSimon Glass debug("%s: started\n", __func__); 278835dd000SBin Meng 27916b195c8SJean-Christophe PLAGNIOL-VILLARD return 0; 28016b195c8SJean-Christophe PLAGNIOL-VILLARD } 28116b195c8SJean-Christophe PLAGNIOL-VILLARD 2822ec739dbSSimon Glass /** 283*dcbf8257SSimon Glass * Set up the i8042 keyboard. This is called by the stdio device handler 284835dd000SBin Meng * 285*dcbf8257SSimon Glass * We want to do this init when the keyboard is actually used rather than 286*dcbf8257SSimon Glass * at start-up, since keyboard input may not currently be selected. 287*dcbf8257SSimon Glass * 288*dcbf8257SSimon Glass * Once the keyboard starts there will be a period during which we must 289*dcbf8257SSimon Glass * wait for the keyboard to init. We do this only when a key is first 290*dcbf8257SSimon Glass * read - see kbd_wait_for_fifo_init(). 291*dcbf8257SSimon Glass * 292*dcbf8257SSimon Glass * @return 0 if ok, -ve on error 29316b195c8SJean-Christophe PLAGNIOL-VILLARD */ 294*dcbf8257SSimon Glass static int i8042_kbd_probe(struct udevice *dev) 2952ec739dbSSimon Glass { 296*dcbf8257SSimon Glass struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); 297*dcbf8257SSimon Glass struct stdio_dev *sdev = &uc_priv->sdev; 298*dcbf8257SSimon Glass struct input_config *input = &uc_priv->input; 299*dcbf8257SSimon Glass int ret; 3002ec739dbSSimon Glass 301*dcbf8257SSimon Glass /* Register the device. i8042_start() will be called soon */ 302*dcbf8257SSimon Glass input->dev = dev; 303*dcbf8257SSimon Glass input->read_keys = i8042_kbd_check; 304*dcbf8257SSimon Glass input_allow_repeats(input, true); 305*dcbf8257SSimon Glass strcpy(sdev->name, "i8042-kbd"); 306*dcbf8257SSimon Glass ret = input_stdio_register(sdev); 307*dcbf8257SSimon Glass if (ret) { 308*dcbf8257SSimon Glass debug("%s: input_stdio_register() failed\n", __func__); 3092ec739dbSSimon Glass return ret; 3102ec739dbSSimon Glass } 311*dcbf8257SSimon Glass debug("%s: ready\n", __func__); 3122ec739dbSSimon Glass 313*dcbf8257SSimon Glass return 0; 31416b195c8SJean-Christophe PLAGNIOL-VILLARD } 315835dd000SBin Meng 316*dcbf8257SSimon Glass static const struct keyboard_ops i8042_kbd_ops = { 317*dcbf8257SSimon Glass .start = i8042_start, 318*dcbf8257SSimon Glass .update_leds = i8042_kbd_update_leds, 319*dcbf8257SSimon Glass }; 320*dcbf8257SSimon Glass 321*dcbf8257SSimon Glass static const struct udevice_id i8042_kbd_ids[] = { 322*dcbf8257SSimon Glass { .compatible = "intel,i8042-keyboard" }, 323*dcbf8257SSimon Glass { } 324*dcbf8257SSimon Glass }; 325*dcbf8257SSimon Glass 326*dcbf8257SSimon Glass U_BOOT_DRIVER(i8042_kbd) = { 327*dcbf8257SSimon Glass .name = "i8042_kbd", 328*dcbf8257SSimon Glass .id = UCLASS_KEYBOARD, 329*dcbf8257SSimon Glass .of_match = i8042_kbd_ids, 330*dcbf8257SSimon Glass .probe = i8042_kbd_probe, 331*dcbf8257SSimon Glass .ops = &i8042_kbd_ops, 332*dcbf8257SSimon Glass .priv_auto_alloc_size = sizeof(struct i8042_kbd_priv), 333*dcbf8257SSimon Glass }; 334