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> 1116b195c8SJean-Christophe PLAGNIOL-VILLARD #include <i8042.h> 12*2ec739dbSSimon Glass #include <input.h> 13*2ec739dbSSimon Glass #include <asm/io.h> 1416b195c8SJean-Christophe PLAGNIOL-VILLARD 1516b195c8SJean-Christophe PLAGNIOL-VILLARD /* defines */ 16835dd000SBin Meng #define in8(p) inb(p) 17835dd000SBin Meng #define out8(p, v) outb(v, p) 1816b195c8SJean-Christophe PLAGNIOL-VILLARD 1916b195c8SJean-Christophe PLAGNIOL-VILLARD /* locals */ 20*2ec739dbSSimon Glass static struct input_config config; 21*2ec739dbSSimon Glass static bool extended; 2216b195c8SJean-Christophe PLAGNIOL-VILLARD 23dd4a5b22SGabe Black static unsigned char ext_key_map[] = { 2416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1c, /* keypad enter */ 2516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x1d, /* right control */ 2616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x35, /* keypad slash */ 2716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x37, /* print screen */ 2816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x38, /* right alt */ 2916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x46, /* break */ 3016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x47, /* editpad home */ 3116b195c8SJean-Christophe PLAGNIOL-VILLARD 0x48, /* editpad up */ 3216b195c8SJean-Christophe PLAGNIOL-VILLARD 0x49, /* editpad pgup */ 3316b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4b, /* editpad left */ 3416b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4d, /* editpad right */ 3516b195c8SJean-Christophe PLAGNIOL-VILLARD 0x4f, /* editpad end */ 3616b195c8SJean-Christophe PLAGNIOL-VILLARD 0x50, /* editpad dn */ 3716b195c8SJean-Christophe PLAGNIOL-VILLARD 0x51, /* editpad pgdn */ 3816b195c8SJean-Christophe PLAGNIOL-VILLARD 0x52, /* editpad ins */ 3916b195c8SJean-Christophe PLAGNIOL-VILLARD 0x53, /* editpad del */ 4016b195c8SJean-Christophe PLAGNIOL-VILLARD 0x00 /* map end */ 4116b195c8SJean-Christophe PLAGNIOL-VILLARD }; 4216b195c8SJean-Christophe PLAGNIOL-VILLARD 433928d66aSBin Meng static int kbd_input_empty(void) 443928d66aSBin Meng { 45835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000; 463928d66aSBin Meng 47835dd000SBin Meng while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--) 483928d66aSBin Meng udelay(1); 493928d66aSBin Meng 50835dd000SBin Meng return kbd_timeout != -1; 513928d66aSBin Meng } 523928d66aSBin Meng 53835dd000SBin Meng static int kbd_output_full(void) 543928d66aSBin Meng { 55835dd000SBin Meng int kbd_timeout = KBD_TIMEOUT * 1000; 563928d66aSBin Meng 57835dd000SBin Meng while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--) 583928d66aSBin Meng udelay(1); 593928d66aSBin Meng 60835dd000SBin Meng return kbd_timeout != -1; 613928d66aSBin Meng } 623928d66aSBin Meng 63*2ec739dbSSimon Glass static void kbd_led_set(int flags) 643928d66aSBin Meng { 653928d66aSBin Meng kbd_input_empty(); 66835dd000SBin Meng out8(I8042_DATA_REG, CMD_SET_KBD_LED); 673928d66aSBin Meng kbd_input_empty(); 68*2ec739dbSSimon Glass out8(I8042_DATA_REG, flags & 0x7); 693928d66aSBin Meng } 703928d66aSBin Meng 7131d38ee6SSimon Glass static int kbd_write(int reg, int value) 7231d38ee6SSimon Glass { 7331d38ee6SSimon Glass if (!kbd_input_empty()) 7431d38ee6SSimon Glass return -1; 7531d38ee6SSimon Glass out8(reg, value); 7631d38ee6SSimon Glass 7731d38ee6SSimon Glass return 0; 7831d38ee6SSimon Glass } 7931d38ee6SSimon Glass 8031d38ee6SSimon Glass static int kbd_read(int reg) 8131d38ee6SSimon Glass { 8231d38ee6SSimon Glass if (!kbd_output_full()) 8331d38ee6SSimon Glass return -1; 8431d38ee6SSimon Glass 8531d38ee6SSimon Glass return in8(reg); 8631d38ee6SSimon Glass } 8731d38ee6SSimon Glass 8831d38ee6SSimon Glass static int kbd_cmd_read(int cmd) 8931d38ee6SSimon Glass { 9031d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd)) 9131d38ee6SSimon Glass return -1; 9231d38ee6SSimon Glass 9331d38ee6SSimon Glass return kbd_read(I8042_DATA_REG); 9431d38ee6SSimon Glass } 9531d38ee6SSimon Glass 9631d38ee6SSimon Glass static int kbd_cmd_write(int cmd, int data) 9731d38ee6SSimon Glass { 9831d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, cmd)) 9931d38ee6SSimon Glass return -1; 10031d38ee6SSimon Glass 10131d38ee6SSimon Glass return kbd_write(I8042_DATA_REG, data); 10231d38ee6SSimon Glass } 10331d38ee6SSimon Glass 1043928d66aSBin Meng static int kbd_reset(void) 1053928d66aSBin Meng { 10631d38ee6SSimon Glass int config; 1077d96166bSBin Meng 1087d96166bSBin Meng /* controller self test */ 10931d38ee6SSimon Glass if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK) 1104f087bacSSimon Glass goto err; 1113928d66aSBin Meng 1127d96166bSBin Meng /* keyboard reset */ 11331d38ee6SSimon Glass if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) || 11431d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_ACK || 11531d38ee6SSimon Glass kbd_read(I8042_DATA_REG) != KBD_POR) 1164f087bacSSimon Glass goto err; 1173928d66aSBin Meng 1187d96166bSBin Meng /* set AT translation and disable irq */ 11931d38ee6SSimon Glass config = kbd_cmd_read(CMD_RD_CONFIG); 12031d38ee6SSimon Glass if (config == -1) 1214f087bacSSimon Glass goto err; 12231d38ee6SSimon Glass 1237d96166bSBin Meng config |= CONFIG_AT_TRANS; 1247d96166bSBin Meng config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN); 12531d38ee6SSimon Glass if (kbd_cmd_write(CMD_WR_CONFIG, config)) 1264f087bacSSimon Glass goto err; 1273928d66aSBin Meng 1287d96166bSBin Meng /* enable keyboard */ 12931d38ee6SSimon Glass if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) || 13031d38ee6SSimon Glass !kbd_input_empty()) 1314f087bacSSimon Glass goto err; 1323928d66aSBin Meng 1333928d66aSBin Meng return 0; 1344f087bacSSimon Glass err: 1354f087bacSSimon Glass debug("%s: Keyboard failure\n", __func__); 1364f087bacSSimon Glass return -1; 1373928d66aSBin Meng } 1383928d66aSBin Meng 13922e0f5a9SGabe Black static int kbd_controller_present(void) 14022e0f5a9SGabe Black { 141835dd000SBin Meng return in8(I8042_STS_REG) != 0xff; 14222e0f5a9SGabe Black } 14322e0f5a9SGabe Black 14448edb304SGabe Black /* 14548edb304SGabe Black * Implement a weak default function for boards that optionally 14648edb304SGabe Black * need to skip the i8042 initialization. 14748edb304SGabe Black */ 14848edb304SGabe Black int __weak board_i8042_skip(void) 14948edb304SGabe Black { 15048edb304SGabe Black /* As default, don't skip */ 15148edb304SGabe Black return 0; 15248edb304SGabe Black } 15348edb304SGabe Black 15445fe668fSLouis Yung-Chieh Lo void i8042_flush(void) 15545fe668fSLouis Yung-Chieh Lo { 15645fe668fSLouis Yung-Chieh Lo int timeout; 15745fe668fSLouis Yung-Chieh Lo 15845fe668fSLouis Yung-Chieh Lo /* 159835dd000SBin Meng * The delay is to give the keyboard controller some time 160835dd000SBin Meng * to fill the next byte. 16145fe668fSLouis Yung-Chieh Lo */ 16245fe668fSLouis Yung-Chieh Lo while (1) { 16345fe668fSLouis Yung-Chieh Lo timeout = 100; /* wait for no longer than 100us */ 164835dd000SBin Meng while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) { 16545fe668fSLouis Yung-Chieh Lo udelay(1); 16645fe668fSLouis Yung-Chieh Lo timeout--; 16745fe668fSLouis Yung-Chieh Lo } 16845fe668fSLouis Yung-Chieh Lo 169835dd000SBin Meng /* Try to pull next byte if not timeout */ 170835dd000SBin Meng if (in8(I8042_STS_REG) & STATUS_OBF) 17145fe668fSLouis Yung-Chieh Lo in8(I8042_DATA_REG); 17245fe668fSLouis Yung-Chieh Lo else 17345fe668fSLouis Yung-Chieh Lo break; 17445fe668fSLouis Yung-Chieh Lo } 17545fe668fSLouis Yung-Chieh Lo } 17645fe668fSLouis Yung-Chieh Lo 17745fe668fSLouis Yung-Chieh Lo int i8042_disable(void) 17845fe668fSLouis Yung-Chieh Lo { 17945fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0) 18045fe668fSLouis Yung-Chieh Lo return -1; 18145fe668fSLouis Yung-Chieh Lo 18245fe668fSLouis Yung-Chieh Lo /* Disable keyboard */ 183835dd000SBin Meng out8(I8042_CMD_REG, CMD_KBD_DIS); 18445fe668fSLouis Yung-Chieh Lo 18545fe668fSLouis Yung-Chieh Lo if (kbd_input_empty() == 0) 18645fe668fSLouis Yung-Chieh Lo return -1; 18745fe668fSLouis Yung-Chieh Lo 18845fe668fSLouis Yung-Chieh Lo return 0; 18945fe668fSLouis Yung-Chieh Lo } 19045fe668fSLouis Yung-Chieh Lo 191*2ec739dbSSimon Glass static int i8042_kbd_check(struct input_config *input) 192*2ec739dbSSimon Glass { 193*2ec739dbSSimon Glass if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) { 194*2ec739dbSSimon Glass return 0; 195*2ec739dbSSimon Glass } else { 196*2ec739dbSSimon Glass bool release = false; 197*2ec739dbSSimon Glass int scan_code; 198*2ec739dbSSimon Glass int i; 199*2ec739dbSSimon Glass 200*2ec739dbSSimon Glass scan_code = in8(I8042_DATA_REG); 201*2ec739dbSSimon Glass if (scan_code == 0xfa) { 202*2ec739dbSSimon Glass return 0; 203*2ec739dbSSimon Glass } else if (scan_code == 0xe0) { 204*2ec739dbSSimon Glass extended = true; 205*2ec739dbSSimon Glass return 0; 206*2ec739dbSSimon Glass } 207*2ec739dbSSimon Glass if (scan_code & 0x80) { 208*2ec739dbSSimon Glass scan_code &= 0x7f; 209*2ec739dbSSimon Glass release = true; 210*2ec739dbSSimon Glass } 211*2ec739dbSSimon Glass if (extended) { 212*2ec739dbSSimon Glass extended = false; 213*2ec739dbSSimon Glass for (i = 0; ext_key_map[i]; i++) { 214*2ec739dbSSimon Glass if (ext_key_map[i] == scan_code) { 215*2ec739dbSSimon Glass scan_code = 0x60 + i; 216*2ec739dbSSimon Glass break; 217*2ec739dbSSimon Glass } 218*2ec739dbSSimon Glass } 219*2ec739dbSSimon Glass /* not found ? */ 220*2ec739dbSSimon Glass if (!ext_key_map[i]) 221*2ec739dbSSimon Glass return 0; 222*2ec739dbSSimon Glass } 223*2ec739dbSSimon Glass 224*2ec739dbSSimon Glass input_add_keycode(&config, scan_code, release); 225*2ec739dbSSimon Glass return 1; 226*2ec739dbSSimon Glass } 227*2ec739dbSSimon Glass } 228*2ec739dbSSimon Glass 229835dd000SBin Meng /* i8042_kbd_init - reset keyboard and init state flags */ 23016b195c8SJean-Christophe PLAGNIOL-VILLARD int i8042_kbd_init(void) 23116b195c8SJean-Christophe PLAGNIOL-VILLARD { 23216b195c8SJean-Christophe PLAGNIOL-VILLARD int keymap, try; 23316b195c8SJean-Christophe PLAGNIOL-VILLARD char *penv; 234*2ec739dbSSimon Glass int ret; 23516b195c8SJean-Christophe PLAGNIOL-VILLARD 236835dd000SBin Meng if (!kbd_controller_present() || board_i8042_skip()) { 237835dd000SBin Meng debug("i8042 keyboard controller is not present\n"); 23822e0f5a9SGabe Black return -1; 239835dd000SBin Meng } 24022e0f5a9SGabe Black 24116b195c8SJean-Christophe PLAGNIOL-VILLARD /* Init keyboard device (default US layout) */ 24216b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_US; 243dd4a5b22SGabe Black penv = getenv("keymap"); 244dd4a5b22SGabe Black if (penv != NULL) { 24516b195c8SJean-Christophe PLAGNIOL-VILLARD if (strncmp(penv, "de", 3) == 0) 24616b195c8SJean-Christophe PLAGNIOL-VILLARD keymap = KBD_GER; 24716b195c8SJean-Christophe PLAGNIOL-VILLARD } 24816b195c8SJean-Christophe PLAGNIOL-VILLARD 249c5d257f9SSimon Glass for (try = 0; kbd_reset() != 0; try++) { 250c5d257f9SSimon Glass if (try >= KBD_RESET_TRIES) 251c5d257f9SSimon Glass return -1; 252c5d257f9SSimon Glass } 253c5d257f9SSimon Glass 254*2ec739dbSSimon Glass ret = input_init(&config, keymap == KBD_GER); 255*2ec739dbSSimon Glass if (ret) 256*2ec739dbSSimon Glass return ret; 257*2ec739dbSSimon Glass config.read_keys = i8042_kbd_check; 258*2ec739dbSSimon Glass input_allow_repeats(&config, true); 259*2ec739dbSSimon Glass 260*2ec739dbSSimon Glass kbd_led_set(NORMAL); 261835dd000SBin Meng 26216b195c8SJean-Christophe PLAGNIOL-VILLARD return 0; 26316b195c8SJean-Christophe PLAGNIOL-VILLARD } 26416b195c8SJean-Christophe PLAGNIOL-VILLARD 265*2ec739dbSSimon Glass /** 266*2ec739dbSSimon Glass * check_leds() - Check the keyboard LEDs and update them it needed 267835dd000SBin Meng * 268*2ec739dbSSimon Glass * @ret: Value to return 269*2ec739dbSSimon Glass * @return value of @ret 27016b195c8SJean-Christophe PLAGNIOL-VILLARD */ 271*2ec739dbSSimon Glass static int check_leds(int ret) 272*2ec739dbSSimon Glass { 273*2ec739dbSSimon Glass int leds; 274*2ec739dbSSimon Glass 275*2ec739dbSSimon Glass leds = input_leds_changed(&config); 276*2ec739dbSSimon Glass if (leds >= 0) 277*2ec739dbSSimon Glass kbd_led_set(leds); 278*2ec739dbSSimon Glass 279*2ec739dbSSimon Glass return ret; 280*2ec739dbSSimon Glass } 281*2ec739dbSSimon Glass 282*2ec739dbSSimon Glass /* i8042_tstc - test if keyboard input is available */ 283709ea543SSimon Glass int i8042_tstc(struct stdio_dev *dev) 28416b195c8SJean-Christophe PLAGNIOL-VILLARD { 285*2ec739dbSSimon Glass return check_leds(input_tstc(&config)); 28616b195c8SJean-Christophe PLAGNIOL-VILLARD } 287835dd000SBin Meng 288*2ec739dbSSimon Glass /* i8042_getc - wait till keyboard input is available */ 289709ea543SSimon Glass int i8042_getc(struct stdio_dev *dev) 29016b195c8SJean-Christophe PLAGNIOL-VILLARD { 291*2ec739dbSSimon Glass return check_leds(input_getc(&config)); 29216b195c8SJean-Christophe PLAGNIOL-VILLARD } 293