uboot/drivers/input/i8042.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * (C) Copyright 2002 ELTEC Elektronik AG
   4 * Frank Gottschling <fgottschling@eltec.de>
   5 */
   6
   7/* i8042.c - Intel 8042 keyboard driver routines */
   8
   9#include <common.h>
  10#include <dm.h>
  11#include <env.h>
  12#include <errno.h>
  13#include <i8042.h>
  14#include <input.h>
  15#include <keyboard.h>
  16#include <log.h>
  17#include <asm/global_data.h>
  18#include <asm/io.h>
  19#include <linux/delay.h>
  20
  21DECLARE_GLOBAL_DATA_PTR;
  22
  23/* defines */
  24#define in8(p)          inb(p)
  25#define out8(p, v)      outb(v, p)
  26
  27enum {
  28        QUIRK_DUP_POR   = 1 << 0,
  29};
  30
  31/* locals */
  32struct i8042_kbd_priv {
  33        bool extended;  /* true if an extended keycode is expected next */
  34        int quirks;     /* quirks that we support */
  35};
  36
  37static unsigned char ext_key_map[] = {
  38        0x1c, /* keypad enter */
  39        0x1d, /* right control */
  40        0x35, /* keypad slash */
  41        0x37, /* print screen */
  42        0x38, /* right alt */
  43        0x46, /* break */
  44        0x47, /* editpad home */
  45        0x48, /* editpad up */
  46        0x49, /* editpad pgup */
  47        0x4b, /* editpad left */
  48        0x4d, /* editpad right */
  49        0x4f, /* editpad end */
  50        0x50, /* editpad dn */
  51        0x51, /* editpad pgdn */
  52        0x52, /* editpad ins */
  53        0x53, /* editpad del */
  54        0x00  /* map end */
  55        };
  56
  57static int kbd_input_empty(void)
  58{
  59        int kbd_timeout = KBD_TIMEOUT * 1000;
  60
  61        while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--)
  62                udelay(1);
  63
  64        return kbd_timeout != -1;
  65}
  66
  67static int kbd_output_full(void)
  68{
  69        int kbd_timeout = KBD_TIMEOUT * 1000;
  70
  71        while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--)
  72                udelay(1);
  73
  74        return kbd_timeout != -1;
  75}
  76
  77/**
  78 * check_leds() - Check the keyboard LEDs and update them it needed
  79 *
  80 * @ret:        Value to return
  81 * @return value of @ret
  82 */
  83static int i8042_kbd_update_leds(struct udevice *dev, int leds)
  84{
  85        kbd_input_empty();
  86        out8(I8042_DATA_REG, CMD_SET_KBD_LED);
  87        kbd_input_empty();
  88        out8(I8042_DATA_REG, leds & 0x7);
  89
  90        return 0;
  91}
  92
  93static int kbd_write(int reg, int value)
  94{
  95        if (!kbd_input_empty())
  96                return -1;
  97        out8(reg, value);
  98
  99        return 0;
 100}
 101
 102static int kbd_read(int reg)
 103{
 104        if (!kbd_output_full())
 105                return -1;
 106
 107        return in8(reg);
 108}
 109
 110static int kbd_cmd_read(int cmd)
 111{
 112        if (kbd_write(I8042_CMD_REG, cmd))
 113                return -1;
 114
 115        return kbd_read(I8042_DATA_REG);
 116}
 117
 118static int kbd_cmd_write(int cmd, int data)
 119{
 120        if (kbd_write(I8042_CMD_REG, cmd))
 121                return -1;
 122
 123        return kbd_write(I8042_DATA_REG, data);
 124}
 125
 126static int kbd_reset(int quirk)
 127{
 128        int config;
 129
 130        /* controller self test */
 131        if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK)
 132                goto err;
 133
 134        /* keyboard reset */
 135        if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) ||
 136            kbd_read(I8042_DATA_REG) != KBD_ACK ||
 137            kbd_read(I8042_DATA_REG) != KBD_POR)
 138                goto err;
 139
 140        if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) ||
 141            kbd_read(I8042_DATA_REG) != KBD_ACK)
 142                goto err;
 143
 144        /* set AT translation and disable irq */
 145        config = kbd_cmd_read(CMD_RD_CONFIG);
 146        if (config == -1)
 147                goto err;
 148
 149        /* Sometimes get a second byte */
 150        else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR)
 151                config = kbd_cmd_read(CMD_RD_CONFIG);
 152
 153        config |= CFG_AT_TRANS;
 154        config &= ~(CFG_KIRQ_EN | CFG_MIRQ_EN);
 155        if (kbd_cmd_write(CMD_WR_CONFIG, config))
 156                goto err;
 157
 158        /* enable keyboard */
 159        if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) ||
 160            !kbd_input_empty())
 161                goto err;
 162
 163        return 0;
 164err:
 165        debug("%s: Keyboard failure\n", __func__);
 166        return -1;
 167}
 168
 169static int kbd_controller_present(void)
 170{
 171        return in8(I8042_STS_REG) != 0xff;
 172}
 173
 174/** Flush all buffer from keyboard controller to host*/
 175static void i8042_flush(void)
 176{
 177        int timeout;
 178
 179        /*
 180         * The delay is to give the keyboard controller some time
 181         * to fill the next byte.
 182         */
 183        while (1) {
 184                timeout = 100;  /* wait for no longer than 100us */
 185                while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) {
 186                        udelay(1);
 187                        timeout--;
 188                }
 189
 190                /* Try to pull next byte if not timeout */
 191                if (in8(I8042_STS_REG) & STATUS_OBF)
 192                        in8(I8042_DATA_REG);
 193                else
 194                        break;
 195        }
 196}
 197
 198/**
 199 * Disables the keyboard so that key strokes no longer generate scancodes to
 200 * the host.
 201 *
 202 * @return 0 if ok, -1 if keyboard input was found while disabling
 203 */
 204static int i8042_disable(void)
 205{
 206        if (kbd_input_empty() == 0)
 207                return -1;
 208
 209        /* Disable keyboard */
 210        out8(I8042_CMD_REG, CMD_KBD_DIS);
 211
 212        if (kbd_input_empty() == 0)
 213                return -1;
 214
 215        return 0;
 216}
 217
 218static int i8042_kbd_check(struct input_config *input)
 219{
 220        struct i8042_kbd_priv *priv = dev_get_priv(input->dev);
 221
 222        if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) {
 223                return 0;
 224        } else {
 225                bool release = false;
 226                int scan_code;
 227                int i;
 228
 229                scan_code = in8(I8042_DATA_REG);
 230                if (scan_code == 0xfa) {
 231                        return 0;
 232                } else if (scan_code == 0xe0) {
 233                        priv->extended = true;
 234                        return 0;
 235                }
 236                if (scan_code & 0x80) {
 237                        scan_code &= 0x7f;
 238                        release = true;
 239                }
 240                if (priv->extended) {
 241                        priv->extended = false;
 242                        for (i = 0; ext_key_map[i]; i++) {
 243                                if (ext_key_map[i] == scan_code) {
 244                                        scan_code = 0x60 + i;
 245                                        break;
 246                                }
 247                        }
 248                        /* not found ? */
 249                        if (!ext_key_map[i])
 250                                return 0;
 251                }
 252
 253                input_add_keycode(input, scan_code, release);
 254                return 1;
 255        }
 256}
 257
 258/* i8042_kbd_init - reset keyboard and init state flags */
 259static int i8042_start(struct udevice *dev)
 260{
 261        struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
 262        struct i8042_kbd_priv *priv = dev_get_priv(dev);
 263        struct input_config *input = &uc_priv->input;
 264        int keymap, try;
 265        char *penv;
 266        int ret;
 267
 268        if (!kbd_controller_present()) {
 269                debug("i8042 keyboard controller is not present\n");
 270                return -ENOENT;
 271        }
 272
 273        /* Init keyboard device (default US layout) */
 274        keymap = KBD_US;
 275        penv = env_get("keymap");
 276        if (penv != NULL) {
 277                if (strncmp(penv, "de", 3) == 0)
 278                        keymap = KBD_GER;
 279        }
 280
 281        for (try = 0; kbd_reset(priv->quirks) != 0; try++) {
 282                if (try >= KBD_RESET_TRIES)
 283                        return -1;
 284        }
 285
 286        ret = input_add_tables(input, keymap == KBD_GER);
 287        if (ret)
 288                return ret;
 289
 290        i8042_kbd_update_leds(dev, NORMAL);
 291        debug("%s: started\n", __func__);
 292
 293        return 0;
 294}
 295
 296static int i8042_kbd_remove(struct udevice *dev)
 297{
 298        if (i8042_disable())
 299                log_debug("i8042_disable() failed. fine, continue.\n");
 300        i8042_flush();
 301
 302        return 0;
 303}
 304
 305/**
 306 * Set up the i8042 keyboard. This is called by the stdio device handler
 307 *
 308 * We want to do this init when the keyboard is actually used rather than
 309 * at start-up, since keyboard input may not currently be selected.
 310 *
 311 * Once the keyboard starts there will be a period during which we must
 312 * wait for the keyboard to init. We do this only when a key is first
 313 * read - see kbd_wait_for_fifo_init().
 314 *
 315 * @return 0 if ok, -ve on error
 316 */
 317static int i8042_kbd_probe(struct udevice *dev)
 318{
 319        struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
 320        struct i8042_kbd_priv *priv = dev_get_priv(dev);
 321        struct stdio_dev *sdev = &uc_priv->sdev;
 322        struct input_config *input = &uc_priv->input;
 323        int ret;
 324
 325        if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev),
 326                            "intel,duplicate-por"))
 327                priv->quirks |= QUIRK_DUP_POR;
 328
 329        /* Register the device. i8042_start() will be called soon */
 330        input->dev = dev;
 331        input->read_keys = i8042_kbd_check;
 332        input_allow_repeats(input, true);
 333        strcpy(sdev->name, "i8042-kbd");
 334        ret = input_stdio_register(sdev);
 335        if (ret) {
 336                debug("%s: input_stdio_register() failed\n", __func__);
 337                return ret;
 338        }
 339        debug("%s: ready\n", __func__);
 340
 341        return 0;
 342}
 343
 344static const struct keyboard_ops i8042_kbd_ops = {
 345        .start  = i8042_start,
 346        .update_leds    = i8042_kbd_update_leds,
 347};
 348
 349static const struct udevice_id i8042_kbd_ids[] = {
 350        { .compatible = "intel,i8042-keyboard" },
 351        { }
 352};
 353
 354U_BOOT_DRIVER(i8042_kbd) = {
 355        .name   = "i8042_kbd",
 356        .id     = UCLASS_KEYBOARD,
 357        .of_match = i8042_kbd_ids,
 358        .probe = i8042_kbd_probe,
 359        .remove = i8042_kbd_remove,
 360        .ops    = &i8042_kbd_ops,
 361        .priv_auto      = sizeof(struct i8042_kbd_priv),
 362};
 363