diff options
Diffstat (limited to 'roms/u-boot/drivers/input')
-rw-r--r-- | roms/u-boot/drivers/input/Kconfig | 85 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/Makefile | 17 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/cros_ec_keyb.c | 255 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/i8042.c | 362 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/input.c | 685 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/key_matrix.c | 190 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/keyboard-uclass.c | 91 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/tegra-kbc.c | 354 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/twl4030.c | 87 | ||||
-rw-r--r-- | roms/u-boot/drivers/input/twl6030.c | 47 |
10 files changed, 2173 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/input/Kconfig b/roms/u-boot/drivers/input/Kconfig new file mode 100644 index 000000000..a17e55e99 --- /dev/null +++ b/roms/u-boot/drivers/input/Kconfig @@ -0,0 +1,85 @@ +config INPUT + bool "Enable input subsystem" + depends on DM + default y + +config SPL_INPUT + bool "Enable input subsystem for SPL" + depends on SPL_DM + +config TPL_INPUT + bool "Enable input subsystem for TPL" + depends on TPL_DM + +config DM_KEYBOARD + bool "Enable driver model keyboard support" + depends on DM + help + This adds a uclass for keyboards and implements keyboard support + using driver model. The API is implemented by keyboard.h and + includes methods to start/stop the device, check for available + input and update LEDs if the keyboard has them. + +config SPL_DM_KEYBOARD + bool "Enable driver model keyboard support for SPL" + depends on SPL_DM + help + This adds a uclass for keyboards and implements keyboard support + using driver model. The API is implemented by keyboard.h and + includes methods to start/stop the device, check for available + input and update LEDs if the keyboard has them. + +config TPL_DM_KEYBOARD + bool "Enable driver model keyboard support for TPL" + depends on TPL_DM + help + This adds a uclass for keyboards and implements keyboard support + using driver model. The API is implemented by keyboard.h and + includes methods to start/stop the device, check for available + input and update LEDs if the keyboard has them. + +config CROS_EC_KEYB + bool "Enable Chrome OS EC keyboard support" + depends on INPUT + help + Most ARM Chromebooks use an EC to provide access to the keyboard. + Messages are used to request key scans from the EC and these are + then decoded into keys by this driver. + +config SPL_CROS_EC_KEYB + bool "Enable Chrome OS EC keyboard support in SPL" + depends on SPL_INPUT + help + Most ARM Chromebooks use an EC to provide access to the keyboard. + Messages are used to request key scans from the EC and these are + then decoded into keys by this driver. + +config TPL_CROS_EC_KEYB + bool "Enable Chrome OS EC keyboard support in TPL" + depends on TPL_INPUT + help + Most ARM Chromebooks use an EC to provide access to the keyboard. + Messages are used to request key scans from the EC and these are + then decoded into keys by this driver. + +config I8042_KEYB + bool "Enable Intel i8042 keyboard support" + depends on DM_KEYBOARD + default X86 + help + This adds a driver for the i8042 keyboard controller, allowing the + keyboard to be used on devices which support this controller. The + driver handles English and German keyboards - set the environment + variable 'keymap' to "de" to select German. Keyboard repeat is + handled by the keyboard itself. + +config TEGRA_KEYBOARD + bool "NVIDIA Tegra internal matrix keyboard controller support" + help + A matrix keyboard connected directly to the internal keyboard + controller on Tegra SoCs. + +config TWL4030_INPUT + bool "Enable TWL4030 Input controller" + help + Enable TWL4030 Input controller diff --git a/roms/u-boot/drivers/input/Makefile b/roms/u-boot/drivers/input/Makefile new file mode 100644 index 000000000..e440c921e --- /dev/null +++ b/roms/u-boot/drivers/input/Makefile @@ -0,0 +1,17 @@ +# SPDX-License-Identifier: GPL-2.0+ +# +# (C) Copyright 2000-2007 +# Wolfgang Denk, DENX Software Engineering, wd@denx.de. + +obj-$(CONFIG_KEYBOARD) += input.o +obj-$(CONFIG_$(SPL_TPL_)CROS_EC_KEYB) += cros_ec_keyb.o +obj-$(CONFIG_$(SPL_TPL_)OF_CONTROL) += key_matrix.o +obj-$(CONFIG_$(SPL_TPL_)DM_KEYBOARD) += input.o keyboard-uclass.o + +ifndef CONFIG_SPL_BUILD + +obj-$(CONFIG_I8042_KEYB) += i8042.o +obj-$(CONFIG_TEGRA_KEYBOARD) += input.o tegra-kbc.o +obj-$(CONFIG_TWL4030_INPUT) += twl4030.o +obj-$(CONFIG_TWL6030_INPUT) += twl6030.o +endif diff --git a/roms/u-boot/drivers/input/cros_ec_keyb.c b/roms/u-boot/drivers/input/cros_ec_keyb.c new file mode 100644 index 000000000..dc3b08c0f --- /dev/null +++ b/roms/u-boot/drivers/input/cros_ec_keyb.c @@ -0,0 +1,255 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Chromium OS Matrix Keyboard + * + * Copyright (c) 2012 The Chromium OS Authors. + */ + +#include <common.h> +#include <cros_ec.h> +#include <dm.h> +#include <errno.h> +#include <input.h> +#include <keyboard.h> +#include <key_matrix.h> +#include <log.h> +#include <stdio_dev.h> + +enum { + KBC_MAX_KEYS = 8, /* Maximum keys held down at once */ + KBC_REPEAT_RATE_MS = 30, + KBC_REPEAT_DELAY_MS = 240, +}; + +struct cros_ec_keyb_priv { + struct input_config *input; /* The input layer */ + struct key_matrix matrix; /* The key matrix layer */ + int key_rows; /* Number of keyboard rows */ + int key_cols; /* Number of keyboard columns */ + int ghost_filter; /* 1 to enable ghost filter, else 0 */ +}; + + +/** + * Check the keyboard controller and return a list of key matrix positions + * for which a key is pressed + * + * @param dev Keyboard device + * @param keys List of keys that we have detected + * @param max_count Maximum number of keys to return + * @param samep Set to true if this scan repeats the last, else false + * @return number of pressed keys, 0 for none, -EIO on error + */ +static int check_for_keys(struct udevice *dev, struct key_matrix_key *keys, + int max_count, bool *samep) +{ + struct cros_ec_keyb_priv *priv = dev_get_priv(dev); + struct key_matrix_key *key; + static struct mbkp_keyscan last_scan; + static bool last_scan_valid; + struct ec_response_get_next_event event; + struct mbkp_keyscan *scan = (struct mbkp_keyscan *) + &event.data.key_matrix; + unsigned int row, col, bit, data; + int num_keys; + int ret; + + /* Get pending MKBP event. It may not be a key matrix event. */ + do { + ret = cros_ec_get_next_event(dev->parent, &event); + /* The EC has no events for us at this time. */ + if (ret == -EC_RES_UNAVAILABLE) + return -EIO; + else if (ret) + break; + } while (event.event_type != EC_MKBP_EVENT_KEY_MATRIX); + + /* Try the old command if the EC doesn't support the above. */ + if (ret == -EC_RES_INVALID_COMMAND) { + if (cros_ec_scan_keyboard(dev->parent, scan)) { + debug("%s: keyboard scan failed\n", __func__); + return -EIO; + } + } else if (ret) { + debug("%s: Error getting next MKBP event. (%d)\n", + __func__, ret); + return -EIO; + } + *samep = last_scan_valid && !memcmp(&last_scan, scan, sizeof(*scan)); + + /* + * This is a bit odd. The EC has no way to tell us that it has run + * out of key scans. It just returns the same scan over and over + * again. So the only way to detect that we have run out is to detect + * that this scan is the same as the last. + */ + last_scan_valid = true; + memcpy(&last_scan, scan, sizeof(last_scan)); + + for (col = num_keys = bit = 0; col < priv->matrix.num_cols; + col++) { + for (row = 0; row < priv->matrix.num_rows; row++) { + unsigned int mask = 1 << (bit & 7); + + data = scan->data[bit / 8]; + if ((data & mask) && num_keys < max_count) { + key = keys + num_keys++; + key->row = row; + key->col = col; + key->valid = 1; + } + bit++; + } + } + + return num_keys; +} + +/** + * Check the keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input Input configuration + * @return 1, to indicate that we have something to look at + */ +int cros_ec_kbc_check(struct input_config *input) +{ + struct udevice *dev = input->dev; + struct cros_ec_keyb_priv *priv = dev_get_priv(dev); + static struct key_matrix_key last_keys[KBC_MAX_KEYS]; + static int last_num_keys; + struct key_matrix_key keys[KBC_MAX_KEYS]; + int keycodes[KBC_MAX_KEYS]; + int num_keys, num_keycodes; + int irq_pending, sent; + bool same = false; + + /* + * Loop until the EC has no more keyscan records, or we have + * received at least one character. This means we know that tstc() + * will always return non-zero if keys have been pressed. + * + * Without this loop, a key release (which generates no new ascii + * characters) will cause us to exit this function, and just tstc() + * may return 0 before all keys have been read from the EC. + */ + do { + irq_pending = cros_ec_interrupt_pending(dev->parent); + if (irq_pending) { + num_keys = check_for_keys(dev, keys, KBC_MAX_KEYS, + &same); + if (num_keys < 0) + return 0; + last_num_keys = num_keys; + memcpy(last_keys, keys, sizeof(keys)); + } else { + /* + * EC doesn't want to be asked, so use keys from last + * time. + */ + num_keys = last_num_keys; + memcpy(keys, last_keys, sizeof(keys)); + } + + if (num_keys < 0) + return -1; + num_keycodes = key_matrix_decode(&priv->matrix, keys, + num_keys, keycodes, KBC_MAX_KEYS); + sent = input_send_keycodes(input, keycodes, num_keycodes); + + /* + * For those ECs without an interrupt, stop scanning when we + * see that the scan is the same as last time. + */ + if ((irq_pending < 0) && same) + break; + } while (irq_pending && !sent); + + return 1; +} + +/** + * Decode MBKP keyboard details from the device tree + * + * @param blob Device tree blob + * @param node Node to decode from + * @param config Configuration data read from fdt + * @return 0 if ok, -1 on error + */ +static int cros_ec_keyb_decode_fdt(struct udevice *dev, + struct cros_ec_keyb_priv *config) +{ + /* + * Get keyboard rows and columns - at present we are limited to + * 8 columns by the protocol (one byte per row scan) + */ + config->key_rows = dev_read_u32_default(dev, "keypad,num-rows", 0); + config->key_cols = dev_read_u32_default(dev, "keypad,num-columns", 0); + if (!config->key_rows || !config->key_cols || + config->key_rows * config->key_cols / 8 + > CROS_EC_KEYSCAN_COLS) { + debug("%s: Invalid key matrix size %d x %d\n", __func__, + config->key_rows, config->key_cols); + return -1; + } + config->ghost_filter = dev_read_bool(dev, "google,needs-ghost-filter"); + + return 0; +} + +static int cros_ec_kbd_probe(struct udevice *dev) +{ + struct cros_ec_keyb_priv *priv = dev_get_priv(dev); + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret; + + ret = cros_ec_keyb_decode_fdt(dev, priv); + if (ret) { + debug("%s: Cannot decode node (ret=%d)\n", __func__, ret); + return -EINVAL; + } + input_set_delays(input, KBC_REPEAT_DELAY_MS, KBC_REPEAT_RATE_MS); + ret = key_matrix_init(&priv->matrix, priv->key_rows, priv->key_cols, + priv->ghost_filter); + if (ret) { + debug("%s: cannot init key matrix\n", __func__); + return ret; + } + ret = key_matrix_decode_fdt(dev, &priv->matrix); + if (ret) { + debug("%s: Could not decode key matrix from fdt\n", __func__); + return ret; + } + debug("%s: Matrix keyboard %dx%d ready\n", __func__, priv->key_rows, + priv->key_cols); + + priv->input = input; + input->dev = dev; + input_add_tables(input, false); + input->read_keys = cros_ec_kbc_check; + strcpy(sdev->name, "cros-ec-keyb"); + + /* Register the device. cros_ec_init_keyboard() will be called soon */ + return input_stdio_register(sdev); +} + +static const struct keyboard_ops cros_ec_kbd_ops = { +}; + +static const struct udevice_id cros_ec_kbd_ids[] = { + { .compatible = "google,cros-ec-keyb" }, + { } +}; + +U_BOOT_DRIVER(google_cros_ec_keyb) = { + .name = "google_cros_ec_keyb", + .id = UCLASS_KEYBOARD, + .of_match = cros_ec_kbd_ids, + .probe = cros_ec_kbd_probe, + .ops = &cros_ec_kbd_ops, + .priv_auto = sizeof(struct cros_ec_keyb_priv), +}; diff --git a/roms/u-boot/drivers/input/i8042.c b/roms/u-boot/drivers/input/i8042.c new file mode 100644 index 000000000..565d99e7e --- /dev/null +++ b/roms/u-boot/drivers/input/i8042.c @@ -0,0 +1,362 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2002 ELTEC Elektronik AG + * Frank Gottschling <fgottschling@eltec.de> + */ + +/* i8042.c - Intel 8042 keyboard driver routines */ + +#include <common.h> +#include <dm.h> +#include <env.h> +#include <errno.h> +#include <i8042.h> +#include <input.h> +#include <keyboard.h> +#include <log.h> +#include <asm/global_data.h> +#include <asm/io.h> +#include <linux/delay.h> + +DECLARE_GLOBAL_DATA_PTR; + +/* defines */ +#define in8(p) inb(p) +#define out8(p, v) outb(v, p) + +enum { + QUIRK_DUP_POR = 1 << 0, +}; + +/* locals */ +struct i8042_kbd_priv { + bool extended; /* true if an extended keycode is expected next */ + int quirks; /* quirks that we support */ +}; + +static unsigned char ext_key_map[] = { + 0x1c, /* keypad enter */ + 0x1d, /* right control */ + 0x35, /* keypad slash */ + 0x37, /* print screen */ + 0x38, /* right alt */ + 0x46, /* break */ + 0x47, /* editpad home */ + 0x48, /* editpad up */ + 0x49, /* editpad pgup */ + 0x4b, /* editpad left */ + 0x4d, /* editpad right */ + 0x4f, /* editpad end */ + 0x50, /* editpad dn */ + 0x51, /* editpad pgdn */ + 0x52, /* editpad ins */ + 0x53, /* editpad del */ + 0x00 /* map end */ + }; + +static int kbd_input_empty(void) +{ + int kbd_timeout = KBD_TIMEOUT * 1000; + + while ((in8(I8042_STS_REG) & STATUS_IBF) && kbd_timeout--) + udelay(1); + + return kbd_timeout != -1; +} + +static int kbd_output_full(void) +{ + int kbd_timeout = KBD_TIMEOUT * 1000; + + while (((in8(I8042_STS_REG) & STATUS_OBF) == 0) && kbd_timeout--) + udelay(1); + + return kbd_timeout != -1; +} + +/** + * check_leds() - Check the keyboard LEDs and update them it needed + * + * @ret: Value to return + * @return value of @ret + */ +static int i8042_kbd_update_leds(struct udevice *dev, int leds) +{ + kbd_input_empty(); + out8(I8042_DATA_REG, CMD_SET_KBD_LED); + kbd_input_empty(); + out8(I8042_DATA_REG, leds & 0x7); + + return 0; +} + +static int kbd_write(int reg, int value) +{ + if (!kbd_input_empty()) + return -1; + out8(reg, value); + + return 0; +} + +static int kbd_read(int reg) +{ + if (!kbd_output_full()) + return -1; + + return in8(reg); +} + +static int kbd_cmd_read(int cmd) +{ + if (kbd_write(I8042_CMD_REG, cmd)) + return -1; + + return kbd_read(I8042_DATA_REG); +} + +static int kbd_cmd_write(int cmd, int data) +{ + if (kbd_write(I8042_CMD_REG, cmd)) + return -1; + + return kbd_write(I8042_DATA_REG, data); +} + +static int kbd_reset(int quirk) +{ + int config; + + /* controller self test */ + if (kbd_cmd_read(CMD_SELF_TEST) != KBC_TEST_OK) + goto err; + + /* keyboard reset */ + if (kbd_write(I8042_DATA_REG, CMD_RESET_KBD) || + kbd_read(I8042_DATA_REG) != KBD_ACK || + kbd_read(I8042_DATA_REG) != KBD_POR) + goto err; + + if (kbd_write(I8042_DATA_REG, CMD_DRAIN_OUTPUT) || + kbd_read(I8042_DATA_REG) != KBD_ACK) + goto err; + + /* set AT translation and disable irq */ + config = kbd_cmd_read(CMD_RD_CONFIG); + if (config == -1) + goto err; + + /* Sometimes get a second byte */ + else if ((quirk & QUIRK_DUP_POR) && config == KBD_POR) + config = kbd_cmd_read(CMD_RD_CONFIG); + + config |= CONFIG_AT_TRANS; + config &= ~(CONFIG_KIRQ_EN | CONFIG_MIRQ_EN); + if (kbd_cmd_write(CMD_WR_CONFIG, config)) + goto err; + + /* enable keyboard */ + if (kbd_write(I8042_CMD_REG, CMD_KBD_EN) || + !kbd_input_empty()) + goto err; + + return 0; +err: + debug("%s: Keyboard failure\n", __func__); + return -1; +} + +static int kbd_controller_present(void) +{ + return in8(I8042_STS_REG) != 0xff; +} + +/** Flush all buffer from keyboard controller to host*/ +static void i8042_flush(void) +{ + int timeout; + + /* + * The delay is to give the keyboard controller some time + * to fill the next byte. + */ + while (1) { + timeout = 100; /* wait for no longer than 100us */ + while (timeout > 0 && !(in8(I8042_STS_REG) & STATUS_OBF)) { + udelay(1); + timeout--; + } + + /* Try to pull next byte if not timeout */ + if (in8(I8042_STS_REG) & STATUS_OBF) + in8(I8042_DATA_REG); + else + break; + } +} + +/** + * Disables the keyboard so that key strokes no longer generate scancodes to + * the host. + * + * @return 0 if ok, -1 if keyboard input was found while disabling + */ +static int i8042_disable(void) +{ + if (kbd_input_empty() == 0) + return -1; + + /* Disable keyboard */ + out8(I8042_CMD_REG, CMD_KBD_DIS); + + if (kbd_input_empty() == 0) + return -1; + + return 0; +} + +static int i8042_kbd_check(struct input_config *input) +{ + struct i8042_kbd_priv *priv = dev_get_priv(input->dev); + + if ((in8(I8042_STS_REG) & STATUS_OBF) == 0) { + return 0; + } else { + bool release = false; + int scan_code; + int i; + + scan_code = in8(I8042_DATA_REG); + if (scan_code == 0xfa) { + return 0; + } else if (scan_code == 0xe0) { + priv->extended = true; + return 0; + } + if (scan_code & 0x80) { + scan_code &= 0x7f; + release = true; + } + if (priv->extended) { + priv->extended = false; + for (i = 0; ext_key_map[i]; i++) { + if (ext_key_map[i] == scan_code) { + scan_code = 0x60 + i; + break; + } + } + /* not found ? */ + if (!ext_key_map[i]) + return 0; + } + + input_add_keycode(input, scan_code, release); + return 1; + } +} + +/* i8042_kbd_init - reset keyboard and init state flags */ +static int i8042_start(struct udevice *dev) +{ + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct i8042_kbd_priv *priv = dev_get_priv(dev); + struct input_config *input = &uc_priv->input; + int keymap, try; + char *penv; + int ret; + + if (!kbd_controller_present()) { + debug("i8042 keyboard controller is not present\n"); + return -ENOENT; + } + + /* Init keyboard device (default US layout) */ + keymap = KBD_US; + penv = env_get("keymap"); + if (penv != NULL) { + if (strncmp(penv, "de", 3) == 0) + keymap = KBD_GER; + } + + for (try = 0; kbd_reset(priv->quirks) != 0; try++) { + if (try >= KBD_RESET_TRIES) + return -1; + } + + ret = input_add_tables(input, keymap == KBD_GER); + if (ret) + return ret; + + i8042_kbd_update_leds(dev, NORMAL); + debug("%s: started\n", __func__); + + return 0; +} + +static int i8042_kbd_remove(struct udevice *dev) +{ + if (i8042_disable()) + log_debug("i8042_disable() failed. fine, continue.\n"); + i8042_flush(); + + return 0; +} + +/** + * Set up the i8042 keyboard. This is called by the stdio device handler + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * Once the keyboard starts there will be a period during which we must + * wait for the keyboard to init. We do this only when a key is first + * read - see kbd_wait_for_fifo_init(). + * + * @return 0 if ok, -ve on error + */ +static int i8042_kbd_probe(struct udevice *dev) +{ + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct i8042_kbd_priv *priv = dev_get_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret; + + if (fdtdec_get_bool(gd->fdt_blob, dev_of_offset(dev), + "intel,duplicate-por")) + priv->quirks |= QUIRK_DUP_POR; + + /* Register the device. i8042_start() will be called soon */ + input->dev = dev; + input->read_keys = i8042_kbd_check; + input_allow_repeats(input, true); + strcpy(sdev->name, "i8042-kbd"); + ret = input_stdio_register(sdev); + if (ret) { + debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + debug("%s: ready\n", __func__); + + return 0; +} + +static const struct keyboard_ops i8042_kbd_ops = { + .start = i8042_start, + .update_leds = i8042_kbd_update_leds, +}; + +static const struct udevice_id i8042_kbd_ids[] = { + { .compatible = "intel,i8042-keyboard" }, + { } +}; + +U_BOOT_DRIVER(i8042_kbd) = { + .name = "i8042_kbd", + .id = UCLASS_KEYBOARD, + .of_match = i8042_kbd_ids, + .probe = i8042_kbd_probe, + .remove = i8042_kbd_remove, + .ops = &i8042_kbd_ops, + .priv_auto = sizeof(struct i8042_kbd_priv), +}; diff --git a/roms/u-boot/drivers/input/input.c b/roms/u-boot/drivers/input/input.c new file mode 100644 index 000000000..c1c5e428d --- /dev/null +++ b/roms/u-boot/drivers/input/input.c @@ -0,0 +1,685 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Translate key codes into ASCII + * + * Copyright (c) 2011 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + */ + +#include <common.h> +#include <console.h> +#include <dm.h> +#include <env.h> +#include <errno.h> +#include <log.h> +#include <stdio_dev.h> +#include <input.h> +#ifdef CONFIG_DM_KEYBOARD +#include <keyboard.h> +#endif +#include <linux/input.h> + +enum { + /* These correspond to the lights on the keyboard */ + FLAG_SCROLL_LOCK = 1 << 0, + FLAG_NUM_LOCK = 1 << 1, + FLAG_CAPS_LOCK = 1 << 2, + + /* Special flag ORed with key code to indicate release */ + KEY_RELEASE = 1 << 15, + KEY_MASK = 0xfff, +}; + +/* + * These takes map key codes to ASCII. 0xff means no key, or special key. + * Three tables are provided - one for plain keys, one for when the shift + * 'modifier' key is pressed and one for when the ctrl modifier key is + * pressed. + */ +static const uchar kbd_plain_xlate[] = { + 0xff, 0x1b, '1', '2', '3', '4', '5', '6', + '7', '8', '9', '0', '-', '=', '\b', '\t', /* 0x00 - 0x0f */ + 'q', 'w', 'e', 'r', 't', 'y', 'u', 'i', + 'o', 'p', '[', ']', '\r', 0xff, 'a', 's', /* 0x10 - 0x1f */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';', + '\'', '`', 0xff, '\\', 'z', 'x', 'c', 'v', /* 0x20 - 0x2f */ + 'b', 'n', 'm', ',' , '.', '/', 0xff, 0xff, 0xff, + ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, '/', '*', +}; + +static unsigned char kbd_shift_xlate[] = { + 0xff, 0x1b, '!', '@', '#', '$', '%', '^', + '&', '*', '(', ')', '_', '+', '\b', '\t', /* 0x00 - 0x0f */ + 'Q', 'W', 'E', 'R', 'T', 'Y', 'U', 'I', + 'O', 'P', '{', '}', '\r', 0xff, 'A', 'S', /* 0x10 - 0x1f */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', ':', + '"', '~', 0xff, '|', 'Z', 'X', 'C', 'V', /* 0x20 - 0x2f */ + 'B', 'N', 'M', '<', '>', '?', 0xff, 0xff, 0xff, + ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, '/', '*', +}; + +static unsigned char kbd_ctrl_xlate[] = { + 0xff, 0x1b, '1', 0x00, '3', '4', '5', 0x1E, + '7', '8', '9', '0', 0x1F, '=', '\b', '\t', /* 0x00 - 0x0f */ + 0x11, 0x17, 0x05, 0x12, 0x14, 0x19, 0x15, 0x09, + 0x0f, 0x10, 0x1b, 0x1d, '\n', 0xff, 0x01, 0x13, /* 0x10 - 0x1f */ + 0x04, 0x06, 0x08, 0x09, 0x0a, 0x0b, 0x0c, ';', + '\'', '~', 0x00, 0x1c, 0x1a, 0x18, 0x03, 0x16, /* 0x20 - 0x2f */ + 0x02, 0x0e, 0x0d, '<', '>', '?', 0xff, 0xff, + 0xff, 0x00, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x30 - 0x3f */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', + '8', '9', '-', '4', '5', '6', '+', '1', /* 0x40 - 0x4f */ + '2', '3', '0', '.', 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 0x50 - 0x5F */ + '\r', 0xff, '/', '*', +}; + +/* + * German keymap. Special letters are mapped according to code page 437. + */ +static const uchar kbd_plain_xlate_german[] = { + 0xff, 0x1b, '1', '2', '3', '4', '5', '6', /* scan 00-07 */ + '7', '8', '9', '0', 0xe1, '\'', 0x08, '\t', /* scan 08-0F */ + 'q', 'w', 'e', 'r', 't', 'z', 'u', 'i', /* scan 10-17 */ + 'o', 'p', 0x81, '+', '\r', 0xff, 'a', 's', /* scan 18-1F */ + 'd', 'f', 'g', 'h', 'j', 'k', 'l', 0x94, /* scan 20-27 */ + 0x84, '^', 0xff, '#', 'y', 'x', 'c', 'v', /* scan 28-2F */ + 'b', 'n', 'm', ',', '.', '-', 0xff, '*', /* scan 30-37 */ + ' ', ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', ',', 0xff, 0xff, '<', 0xff, /* scan 50-57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */ + '\r', 0xff, '/', '*', +}; + +static unsigned char kbd_shift_xlate_german[] = { + 0xff, 0x1b, '!', '"', 0x15, '$', '%', '&', /* scan 00-07 */ + '/', '(', ')', '=', '?', '`', 0x08, '\t', /* scan 08-0F */ + 'Q', 'W', 'E', 'R', 'T', 'Z', 'U', 'I', /* scan 10-17 */ + 'O', 'P', 0x9a, '*', '\r', 0xff, 'A', 'S', /* scan 18-1F */ + 'D', 'F', 'G', 'H', 'J', 'K', 'L', 0x99, /* scan 20-27 */ + 0x8e, 0xf8, 0xff, '\'', 'Y', 'X', 'C', 'V', /* scan 28-2F */ + 'B', 'N', 'M', ';', ':', '_', 0xff, '*', /* scan 30-37 */ + ' ', ' ', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '7', /* scan 40-47 */ + '8', '9', '-', '4', '5', '6', '+', '1', /* scan 48-4F */ + '2', '3', '0', ',', 0xff, 0xff, '>', 0xff, /* scan 50-57 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 58-5F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 60-67 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 68-6F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 70-77 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 78-7F */ + '\r', 0xff, '/', '*', +}; + +static unsigned char kbd_right_alt_xlate_german[] = { + 0xff, 0xff, 0xff, 0xfd, 0xff, 0xff, 0xff, 0xff, /* scan 00-07 */ + '{', '[', ']', '}', '\\', 0xff, 0xff, 0xff, /* scan 08-0F */ + '@', 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 10-17 */ + 0xff, 0xff, 0xff, '~', 0xff, 0xff, 0xff, 0xff, /* scan 18-1F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 20-27 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 28-2F */ + 0xff, 0xff, 0xe6, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 30-37 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 38-3F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 40-47 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* scan 48-4F */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, '|', 0xff, /* scan 50-57 */ +}; + +enum kbd_mask { + KBD_ENGLISH = 1 << 0, + KBD_GERMAN = 1 << 1, +}; + +static struct kbd_entry { + int kbd_mask; /* Which languages this is for */ + int left_keycode; /* Left keycode to select this map */ + int right_keycode; /* Right keycode to select this map */ + const uchar *xlate; /* Ascii code for each keycode */ + int num_entries; /* Number of entries in xlate */ +} kbd_entry[] = { + { KBD_ENGLISH, -1, -1, + kbd_plain_xlate, ARRAY_SIZE(kbd_plain_xlate) }, + { KBD_GERMAN, -1, -1, + kbd_plain_xlate_german, ARRAY_SIZE(kbd_plain_xlate_german) }, + { KBD_ENGLISH, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, + kbd_shift_xlate, ARRAY_SIZE(kbd_shift_xlate) }, + { KBD_GERMAN, KEY_LEFTSHIFT, KEY_RIGHTSHIFT, + kbd_shift_xlate_german, ARRAY_SIZE(kbd_shift_xlate_german) }, + { KBD_ENGLISH | KBD_GERMAN, KEY_LEFTCTRL, KEY_RIGHTCTRL, + kbd_ctrl_xlate, ARRAY_SIZE(kbd_ctrl_xlate) }, + { KBD_GERMAN, -1, KEY_RIGHTALT, + kbd_right_alt_xlate_german, + ARRAY_SIZE(kbd_right_alt_xlate_german) }, + {}, +}; + +/* + * The table contains conversions from scan key codes to ECMA-48 escape + * sequences. The same sequences exist in the withdrawn ANSI 3.64 standard. + * + * As all escape sequences start with 0x1b this byte has been removed. + * + * This table is incomplete in that it does not include all possible extra keys. + */ +static struct { + int kbd_scan_code; + char *escape; +} kbd_to_ansi364[] = { + { KEY_UP, "[A"}, + { KEY_LEFT, "[D"}, + { KEY_RIGHT, "[C"}, + { KEY_DOWN, "[B"}, + { KEY_F1, "OP"}, + { KEY_F2, "OQ"}, + { KEY_F3, "OR"}, + { KEY_F4, "OS"}, + { KEY_F5, "[15~"}, + { KEY_F6, "[17~"}, + { KEY_F7, "[18~"}, + { KEY_F8, "[19~"}, + { KEY_F9, "[20~"}, + { KEY_F10, "[21~"}, +}; + +/* Maximum number of output characters that an ANSI sequence expands to */ +#define ANSI_CHAR_MAX 5 + +static int input_queue_ascii(struct input_config *config, int ch) +{ + if (config->fifo_in + 1 == INPUT_BUFFER_LEN) { + if (!config->fifo_out) + return -1; /* buffer full */ + else + config->fifo_in = 0; + } else { + if (config->fifo_in + 1 == config->fifo_out) + return -1; /* buffer full */ + config->fifo_in++; + } + debug(" {%02x} ", ch); + config->fifo[config->fifo_in] = (uchar)ch; + + return 0; +} + +int input_tstc(struct input_config *config) +{ + if (config->fifo_in == config->fifo_out && config->read_keys) { + if (!(*config->read_keys)(config)) + return 0; + } + return config->fifo_in != config->fifo_out; +} + +int input_getc(struct input_config *config) +{ + int err = 0; + + while (config->fifo_in == config->fifo_out) { + if (config->read_keys) + err = (*config->read_keys)(config); + if (err) + return -1; + } + + if (++config->fifo_out == INPUT_BUFFER_LEN) + config->fifo_out = 0; + + return config->fifo[config->fifo_out]; +} + +/** + * Process a modifier/special key press or release and decide which key + * translation array should be used as a result. + * + * TODO: Should keep track of modifier press/release + * + * @param config Input state + * @param key Key code to process + * @param release 0 if a press, 1 if a release + * @return pointer to keycode->ascii translation table that should be used + */ +static struct input_key_xlate *process_modifier(struct input_config *config, + int key, int release) +{ +#ifdef CONFIG_DM_KEYBOARD + struct udevice *dev = config->dev; + struct keyboard_ops *ops = keyboard_get_ops(dev); +#endif + struct input_key_xlate *table; + int i; + + /* Start with the main table, and see what modifiers change it */ + assert(config->num_tables > 0); + table = &config->table[0]; + for (i = 1; i < config->num_tables; i++) { + struct input_key_xlate *tab = &config->table[i]; + + if (key == tab->left_keycode || key == tab->right_keycode) + table = tab; + } + + /* Handle the lighted keys */ + if (!release) { + int flip = -1; + + switch (key) { + case KEY_SCROLLLOCK: + flip = FLAG_SCROLL_LOCK; + break; + case KEY_NUMLOCK: + flip = FLAG_NUM_LOCK; + break; + case KEY_CAPSLOCK: + flip = FLAG_CAPS_LOCK; + break; + } + + if (flip != -1) { + int leds = 0; + + config->flags ^= flip; + if (config->flags & FLAG_NUM_LOCK) + leds |= INPUT_LED_NUM; + if (config->flags & FLAG_CAPS_LOCK) + leds |= INPUT_LED_CAPS; + if (config->flags & FLAG_SCROLL_LOCK) + leds |= INPUT_LED_SCROLL; + config->leds = leds; + config->leds_changed = flip; + +#ifdef CONFIG_DM_KEYBOARD + if (ops->update_leds) { + if (ops->update_leds(dev, config->leds)) + debug("Update keyboard's LED failed\n"); + } +#endif + } + } + + return table; +} + +/** + * Search an int array for a key value + * + * @param array Array to search + * @param count Number of elements in array + * @param key Key value to find + * @return element where value was first found, -1 if none + */ +static int array_search(int *array, int count, int key) +{ + int i; + + for (i = 0; i < count; i++) { + if (array[i] == key) + return i; + } + + return -1; +} + +/** + * Sort an array so that those elements that exist in the ordering are + * first in the array, and in the same order as the ordering. The algorithm + * is O(count * ocount) and designed for small arrays. + * + * TODO: Move this to common / lib? + * + * @param dest Array with elements to sort, also destination array + * @param count Number of elements to sort + * @param order Array containing ordering elements + * @param ocount Number of ordering elements + * @return number of elements in dest that are in order (these will be at the + * start of dest). + */ +static int sort_array_by_ordering(int *dest, int count, int *order, + int ocount) +{ + int temp[count]; + int dest_count; + int same; /* number of elements which are the same */ + int i; + + /* setup output items, copy items to be sorted into our temp area */ + memcpy(temp, dest, count * sizeof(*dest)); + dest_count = 0; + + /* work through the ordering, move over the elements we agree on */ + for (i = 0; i < ocount; i++) { + if (array_search(temp, count, order[i]) != -1) + dest[dest_count++] = order[i]; + } + same = dest_count; + + /* now move over the elements that are not in the ordering */ + for (i = 0; i < count; i++) { + if (array_search(order, ocount, temp[i]) == -1) + dest[dest_count++] = temp[i]; + } + assert(dest_count == count); + return same; +} + +/** + * Check a list of key codes against the previous key scan + * + * Given a list of new key codes, we check how many of these are the same + * as last time. + * + * @param config Input state + * @param keycode List of key codes to examine + * @param num_keycodes Number of key codes + * @param same Returns number of key codes which are the same + */ +static int input_check_keycodes(struct input_config *config, + int keycode[], int num_keycodes, int *same) +{ + /* Select the 'plain' xlate table to start with */ + if (!config->num_tables) { + debug("%s: No xlate tables: cannot decode keys\n", __func__); + return -1; + } + + /* sort the keycodes into the same order as the previous ones */ + *same = sort_array_by_ordering(keycode, num_keycodes, + config->prev_keycodes, config->num_prev_keycodes); + + memcpy(config->prev_keycodes, keycode, num_keycodes * sizeof(int)); + config->num_prev_keycodes = num_keycodes; + + return *same != num_keycodes; +} + +/** + * Checks and converts a special key code into ANSI 3.64 escape sequence. + * + * @param config Input state + * @param keycode Key code to examine + * @param output_ch Buffer to place output characters into. It should + * be at least ANSI_CHAR_MAX bytes long, to allow for + * an ANSI sequence. + * @param max_chars Maximum number of characters to add to output_ch + * @return number of characters output, if the key was converted, otherwise 0. + * This may be larger than max_chars, in which case the overflow + * characters are not output. + */ +static int input_keycode_to_ansi364(struct input_config *config, + int keycode, char output_ch[], int max_chars) +{ + const char *escape; + int ch_count; + int i; + + for (i = ch_count = 0; i < ARRAY_SIZE(kbd_to_ansi364); i++) { + if (keycode != kbd_to_ansi364[i].kbd_scan_code) + continue; + output_ch[ch_count++] = 0x1b; + for (escape = kbd_to_ansi364[i].escape; *escape; escape++) { + if (ch_count < max_chars) + output_ch[ch_count] = *escape; + ch_count++; + } + return ch_count; + } + + return 0; +} + +/** + * Converts and queues a list of key codes in escaped ASCII string form + * Convert a list of key codes into ASCII + * + * You must call input_check_keycodes() before this. It turns the keycode + * list into a list of ASCII characters and sends them to the input layer. + * + * Characters which were seen last time do not generate fresh ASCII output. + * The output (calls to queue_ascii) may be longer than num_keycodes, if the + * keycode contains special keys that was encoded to longer escaped sequence. + * + * @param config Input state + * @param keycode List of key codes to examine + * @param num_keycodes Number of key codes + * @param output_ch Buffer to place output characters into. It should + * be at last ANSI_CHAR_MAX * num_keycodes, to allow for + * ANSI sequences. + * @param max_chars Maximum number of characters to add to output_ch + * @param same Number of key codes which are the same + * @return number of characters written into output_ch, or -1 if we would + * exceed max_chars chars. + */ +static int input_keycodes_to_ascii(struct input_config *config, + int keycode[], int num_keycodes, char output_ch[], + int max_chars, int same) +{ + struct input_key_xlate *table; + int ch_count = 0; + int i; + + table = &config->table[0]; + + /* deal with modifiers first */ + for (i = 0; i < num_keycodes; i++) { + int key = keycode[i] & KEY_MASK; + + if (key >= table->num_entries || table->xlate[key] == 0xff) { + table = process_modifier(config, key, + keycode[i] & KEY_RELEASE); + } + } + + /* Start conversion by looking for the first new keycode (by same). */ + for (i = same; i < num_keycodes; i++) { + int key = keycode[i]; + int ch = 0xff; + + /* + * For a normal key (with an ASCII value), add it; otherwise + * translate special key to escape sequence if possible. + */ + if (key < table->num_entries) { + ch = table->xlate[key]; + if ((config->flags & FLAG_CAPS_LOCK) && + ch >= 'a' && ch <= 'z') + ch -= 'a' - 'A'; + /* ban digit numbers if 'Num Lock' is not on */ + if (!(config->flags & FLAG_NUM_LOCK)) { + if (key >= KEY_KP7 && key <= KEY_KPDOT && + key != KEY_KPMINUS && key != KEY_KPPLUS) + ch = 0xff; + } + if (ch_count < max_chars && ch != 0xff) + output_ch[ch_count++] = (uchar)ch; + } + if (ch == 0xff) + ch_count += input_keycode_to_ansi364(config, key, + output_ch, max_chars); + } + + if (ch_count > max_chars) { + debug("%s: Output char buffer overflow size=%d, need=%d\n", + __func__, max_chars, ch_count); + return -1; + } + + /* ok, so return keys */ + return ch_count; +} + +static int _input_send_keycodes(struct input_config *config, int keycode[], + int num_keycodes, bool do_send) +{ + char ch[num_keycodes * ANSI_CHAR_MAX]; + int count, i, same = 0; + int is_repeat = 0; + unsigned delay_ms; + + config->modifiers = 0; + if (!input_check_keycodes(config, keycode, num_keycodes, &same)) { + /* + * Same as last time - is it time for another repeat? + * TODO(sjg@chromium.org) We drop repeats here and since + * the caller may not call in again for a while, our + * auto-repeat speed is not quite correct. We should + * insert another character if we later realise that we + * have missed a repeat slot. + */ + is_repeat = config->allow_repeats || (config->repeat_rate_ms && + (int)get_timer(config->next_repeat_ms) >= 0); + if (!is_repeat) + return 0; + } + + count = input_keycodes_to_ascii(config, keycode, num_keycodes, + ch, sizeof(ch), is_repeat ? 0 : same); + if (do_send) { + for (i = 0; i < count; i++) + input_queue_ascii(config, ch[i]); + } + delay_ms = is_repeat ? + config->repeat_rate_ms : + config->repeat_delay_ms; + + config->next_repeat_ms = get_timer(0) + delay_ms; + + return count; +} + +int input_send_keycodes(struct input_config *config, int keycode[], + int num_keycodes) +{ + return _input_send_keycodes(config, keycode, num_keycodes, true); +} + +int input_add_keycode(struct input_config *config, int new_keycode, + bool release) +{ + int keycode[INPUT_MAX_MODIFIERS + 1]; + int count, i; + + /* Add the old keycodes which are not removed by this new one */ + for (i = 0, count = 0; i < config->num_prev_keycodes; i++) { + int code = config->prev_keycodes[i]; + + if (new_keycode == code) { + if (release) + continue; + new_keycode = -1; + } + keycode[count++] = code; + } + + if (!release && new_keycode != -1) + keycode[count++] = new_keycode; + debug("\ncodes for %02x/%d: ", new_keycode, release); + for (i = 0; i < count; i++) + debug("%02x ", keycode[i]); + debug("\n"); + + /* Don't output any ASCII characters if this is a key release */ + return _input_send_keycodes(config, keycode, count, !release); +} + +int input_add_table(struct input_config *config, int left_keycode, + int right_keycode, const uchar *xlate, int num_entries) +{ + struct input_key_xlate *table; + + if (config->num_tables == INPUT_MAX_MODIFIERS) { + debug("%s: Too many modifier tables\n", __func__); + return -1; + } + + table = &config->table[config->num_tables++]; + table->left_keycode = left_keycode; + table->right_keycode = right_keycode; + table->xlate = xlate; + table->num_entries = num_entries; + + return 0; +} + +void input_set_delays(struct input_config *config, int repeat_delay_ms, + int repeat_rate_ms) +{ + config->repeat_delay_ms = repeat_delay_ms; + config->repeat_rate_ms = repeat_rate_ms; +} + +void input_allow_repeats(struct input_config *config, bool allow_repeats) +{ + config->allow_repeats = allow_repeats; +} + +int input_leds_changed(struct input_config *config) +{ + if (config->leds_changed) + return config->leds; + + return -1; +} + +int input_add_tables(struct input_config *config, bool german) +{ + struct kbd_entry *entry; + int mask; + int ret; + + mask = german ? KBD_GERMAN : KBD_ENGLISH; + for (entry = kbd_entry; entry->kbd_mask; entry++) { + if (!(mask & entry->kbd_mask)) + continue; + ret = input_add_table(config, entry->left_keycode, + entry->right_keycode, entry->xlate, + entry->num_entries); + if (ret) + return ret; + } + + return 0; +} + +int input_init(struct input_config *config, int leds) +{ + memset(config, '\0', sizeof(*config)); + config->leds = leds; + + return 0; +} + +int input_stdio_register(struct stdio_dev *dev) +{ + int error; + + error = stdio_register(dev); +#if !defined(CONFIG_SPL_BUILD) || CONFIG_IS_ENABLED(ENV_SUPPORT) + /* check if this is the standard input device */ + if (!error && strcmp(env_get("stdin"), dev->name) == 0) { + /* reassign the console */ + if (OVERWRITE_CONSOLE || + console_assign(stdin, dev->name)) + return -1; + } +#else + error = error; +#endif + + return 0; +} diff --git a/roms/u-boot/drivers/input/key_matrix.c b/roms/u-boot/drivers/input/key_matrix.c new file mode 100644 index 000000000..4631728b8 --- /dev/null +++ b/roms/u-boot/drivers/input/key_matrix.c @@ -0,0 +1,190 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Manage Keyboard Matrices + * + * Copyright (c) 2012 The Chromium OS Authors. + * (C) Copyright 2004 DENX Software Engineering, Wolfgang Denk, wd@denx.de + */ + +#include <common.h> +#include <dm.h> +#include <key_matrix.h> +#include <log.h> +#include <malloc.h> +#include <linux/input.h> + +/** + * Determine if the current keypress configuration can cause key ghosting + * + * We figure this out by seeing if we have two or more keys in the same + * column, as well as two or more keys in the same row. + * + * @param config Keyboard matrix config + * @param keys List of keys to check + * @param valid Number of valid keypresses to check + * @return 0 if no ghosting is possible, 1 if it is + */ +static int has_ghosting(struct key_matrix *config, struct key_matrix_key *keys, + int valid) +{ + int key_in_same_col = 0, key_in_same_row = 0; + int i, j; + + if (!config->ghost_filter || valid < 3) + return 0; + + for (i = 0; i < valid; i++) { + /* + * Find 2 keys such that one key is in the same row + * and the other is in the same column as the i-th key. + */ + for (j = i + 1; j < valid; j++) { + if (keys[j].col == keys[i].col) + key_in_same_col = 1; + if (keys[j].row == keys[i].row) + key_in_same_row = 1; + } + } + + if (key_in_same_col && key_in_same_row) + return 1; + else + return 0; +} + +int key_matrix_decode(struct key_matrix *config, struct key_matrix_key keys[], + int num_keys, int keycode[], int max_keycodes) +{ + const u8 *keymap; + int valid, upto; + int pos; + + debug("%s: num_keys = %d\n", __func__, num_keys); + keymap = config->plain_keycode; + for (valid = upto = 0; upto < num_keys; upto++) { + struct key_matrix_key *key = &keys[upto]; + + debug(" valid=%d, row=%d, col=%d\n", key->valid, key->row, + key->col); + if (!key->valid) + continue; + pos = key->row * config->num_cols + key->col; + if (config->fn_keycode && pos == config->fn_pos) + keymap = config->fn_keycode; + + /* Convert the (row, col) values into a keycode */ + if (valid < max_keycodes) + keycode[valid++] = keymap[pos]; + debug(" keycode=%d\n", keymap[pos]); + } + + /* For a ghost key config, ignore the keypresses for this iteration. */ + if (has_ghosting(config, keys, valid)) { + valid = 0; + debug(" ghosting detected!\n"); + } + debug(" %d valid keycodes found\n", valid); + + return valid; +} + +/** + * Create a new keycode map from some provided data + * + * This decodes a keycode map in the format used by the fdt, which is one + * word per entry, with the row, col and keycode encoded in that word. + * + * We create a (row x col) size byte array with each entry containing the + * keycode for that (row, col). We also search for map_keycode and return + * its position if found (this is used for finding the Fn key). + * + * @param config Key matrix dimensions structure + * @param data Keycode data + * @param len Number of entries in keycode table + * @param map_keycode Key code to find in the map + * @param pos Returns position of map_keycode, if found, else -1 + * @return map Pointer to allocated map + */ +static uchar *create_keymap(struct key_matrix *config, const u32 *data, int len, + int map_keycode, int *pos) +{ + uchar *map; + + if (pos) + *pos = -1; + map = (uchar *)calloc(1, config->key_count); + if (!map) { + debug("%s: failed to malloc %d bytes\n", __func__, + config->key_count); + return NULL; + } + + for (; len >= sizeof(u32); data++, len -= 4) { + u32 tmp = fdt32_to_cpu(*data); + int key_code, row, col; + int entry; + + row = (tmp >> 24) & 0xff; + col = (tmp >> 16) & 0xff; + key_code = tmp & 0xffff; + entry = row * config->num_cols + col; + map[entry] = key_code; + debug(" map %d, %d: pos=%d, keycode=%d\n", row, col, + entry, key_code); + if (pos && map_keycode == key_code) + *pos = entry; + } + + return map; +} + +int key_matrix_decode_fdt(struct udevice *dev, struct key_matrix *config) +{ + const u32 *prop; + int proplen; + uchar *plain_keycode; + + prop = dev_read_prop(dev, "linux,keymap", &proplen); + /* Basic keymap is required */ + if (!prop) { + debug("%s: cannot find keycode-plain map\n", __func__); + return -1; + } + + plain_keycode = create_keymap(config, prop, proplen, KEY_FN, + &config->fn_pos); + config->plain_keycode = plain_keycode; + /* Conversion error -> fail */ + if (!config->plain_keycode) + return -1; + + prop = dev_read_prop(dev, "linux,fn-keymap", &proplen); + /* fn keymap is optional */ + if (!prop) + goto done; + + config->fn_keycode = create_keymap(config, prop, proplen, -1, NULL); + /* Conversion error -> fail */ + if (!config->fn_keycode) { + free(plain_keycode); + return -1; + } + +done: + debug("%s: Decoded key maps %p, %p from fdt\n", __func__, + config->plain_keycode, config->fn_keycode); + return 0; +} + +int key_matrix_init(struct key_matrix *config, int rows, int cols, + int ghost_filter) +{ + memset(config, '\0', sizeof(*config)); + config->num_rows = rows; + config->num_cols = cols; + config->key_count = rows * cols; + config->ghost_filter = ghost_filter; + assert(config->key_count > 0); + + return 0; +} diff --git a/roms/u-boot/drivers/input/keyboard-uclass.c b/roms/u-boot/drivers/input/keyboard-uclass.c new file mode 100644 index 000000000..2c6680337 --- /dev/null +++ b/roms/u-boot/drivers/input/keyboard-uclass.c @@ -0,0 +1,91 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * Copyright (c) 2015 Google, Inc + */ + +#include <common.h> +#include <dm.h> +#include <keyboard.h> +#include <log.h> + +static int keyboard_start(struct stdio_dev *sdev) +{ + struct udevice *dev = sdev->priv; + struct keyboard_ops *ops = keyboard_get_ops(dev); + + if (ops->start) + return ops->start(dev); + + return 0; +} + +static int keyboard_stop(struct stdio_dev *sdev) +{ + struct udevice *dev = sdev->priv; + struct keyboard_ops *ops = keyboard_get_ops(dev); + + if (ops->stop) + return ops->stop(dev); + + return 0; +} + +static int keyboard_tstc(struct stdio_dev *sdev) +{ + struct udevice *dev = sdev->priv; + struct keyboard_priv *priv = dev_get_uclass_priv(dev); + struct keyboard_ops *ops = keyboard_get_ops(dev); + + /* Just get input to do this for us if we can */ + if (priv->input.dev) + return input_tstc(&priv->input); + else if (ops->tstc) + return ops->tstc(dev); + + return -ENOSYS; +} + +static int keyboard_getc(struct stdio_dev *sdev) +{ + struct udevice *dev = sdev->priv; + struct keyboard_priv *priv = dev_get_uclass_priv(dev); + struct keyboard_ops *ops = keyboard_get_ops(dev); + + /* Just get input to do this for us if we can */ + if (priv->input.dev) + return input_getc(&priv->input); + else if (ops->getc) + return ops->getc(dev); + + return -ENOSYS; +} + +static int keyboard_pre_probe(struct udevice *dev) +{ + struct keyboard_priv *priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &priv->sdev; + int ret; + + strlcpy(sdev->name, dev->name, sizeof(sdev->name)); + sdev->flags = DEV_FLAGS_INPUT; + sdev->getc = keyboard_getc; + sdev->tstc = keyboard_tstc; + sdev->start = keyboard_start; + sdev->stop = keyboard_stop; + sdev->priv = dev; + ret = input_init(&priv->input, 0); + if (ret) { + debug("%s: Cannot set up input, ret=%d - please add DEBUG to drivers/input/input.c to figure out the cause\n", + __func__, ret); + return ret; + } + + return 0; +} + +UCLASS_DRIVER(keyboard) = { + .id = UCLASS_KEYBOARD, + .name = "keyboard", + .pre_probe = keyboard_pre_probe, + .per_device_auto = sizeof(struct keyboard_priv), +}; diff --git a/roms/u-boot/drivers/input/tegra-kbc.c b/roms/u-boot/drivers/input/tegra-kbc.c new file mode 100644 index 000000000..5113041d3 --- /dev/null +++ b/roms/u-boot/drivers/input/tegra-kbc.c @@ -0,0 +1,354 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * (C) Copyright 2011 + * NVIDIA Corporation <www.nvidia.com> + */ + +#include <common.h> +#include <dm.h> +#include <fdtdec.h> +#include <input.h> +#include <keyboard.h> +#include <key_matrix.h> +#include <log.h> +#include <stdio_dev.h> +#include <tegra-kbc.h> +#include <asm/io.h> +#include <asm/arch/clock.h> +#include <asm/arch/funcmux.h> +#include <asm/arch-tegra/timer.h> +#include <linux/delay.h> +#include <linux/input.h> + +enum { + KBC_MAX_GPIO = 24, + KBC_MAX_KPENT = 8, /* size of keypress entry queue */ +}; + +#define KBC_FIFO_TH_CNT_SHIFT 14 +#define KBC_DEBOUNCE_CNT_SHIFT 4 +#define KBC_CONTROL_FIFO_CNT_INT_EN (1 << 3) +#define KBC_CONTROL_KBC_EN (1 << 0) +#define KBC_INT_FIFO_CNT_INT_STATUS (1 << 2) +#define KBC_KPENT_VALID (1 << 7) +#define KBC_ST_STATUS (1 << 3) + +enum { + KBC_DEBOUNCE_COUNT = 2, + KBC_REPEAT_RATE_MS = 30, + KBC_REPEAT_DELAY_MS = 240, + KBC_CLOCK_KHZ = 32, /* Keyboard uses a 32KHz clock */ +}; + +/* keyboard controller config and state */ +struct tegra_kbd_priv { + struct input_config *input; /* The input layer */ + struct key_matrix matrix; /* The key matrix layer */ + + struct kbc_tegra *kbc; /* tegra keyboard controller */ + unsigned char inited; /* 1 if keyboard has been inited */ + unsigned char first_scan; /* 1 if this is our first key scan */ + + /* + * After init we must wait a short time before polling the keyboard. + * This gives the tegra keyboard controller time to react after reset + * and lets us grab keys pressed during reset. + */ + unsigned int init_dly_ms; /* Delay before we can read keyboard */ + unsigned int start_time_ms; /* Time that we inited (in ms) */ + unsigned int last_poll_ms; /* Time we should last polled */ + unsigned int next_repeat_ms; /* Next time we repeat a key */ +}; + +/** + * reads the keyboard fifo for current keypresses + * + * @param priv Keyboard private data + * @param fifo Place to put fifo results + * @param max_keycodes Maximum number of key codes to put in the fifo + * @return number of items put into fifo + */ +static int tegra_kbc_find_keys(struct tegra_kbd_priv *priv, int *fifo, + int max_keycodes) +{ + struct key_matrix_key keys[KBC_MAX_KPENT], *key; + u32 kp_ent = 0; + int i; + + for (key = keys, i = 0; i < KBC_MAX_KPENT; i++, key++) { + /* Get next word */ + if (!(i & 3)) + kp_ent = readl(&priv->kbc->kp_ent[i / 4]); + + key->valid = (kp_ent & KBC_KPENT_VALID) != 0; + key->row = (kp_ent >> 3) & 0xf; + key->col = kp_ent & 0x7; + + /* Shift to get next entry */ + kp_ent >>= 8; + } + return key_matrix_decode(&priv->matrix, keys, KBC_MAX_KPENT, fifo, + max_keycodes); +} + +/** + * Process all the keypress sequences in fifo and send key codes + * + * The fifo contains zero or more keypress sets. Each set + * consists of from 1-8 keycodes, representing the keycodes which + * were simultaneously pressed during that scan. + * + * This function works through each set and generates ASCII characters + * for each. Not that one set may produce more than one ASCII characters - + * for example holding down 'd' and 'f' at the same time will generate + * two ASCII characters. + * + * Note: if fifo_cnt is 0, we will tell the input layer that no keys are + * pressed. + * + * @param priv Keyboard private data + * @param fifo_cnt Number of entries in the keyboard fifo + */ +static void process_fifo(struct tegra_kbd_priv *priv, int fifo_cnt) +{ + int fifo[KBC_MAX_KPENT]; + int cnt = 0; + + /* Always call input_send_keycodes() at least once */ + do { + if (fifo_cnt) + cnt = tegra_kbc_find_keys(priv, fifo, KBC_MAX_KPENT); + + input_send_keycodes(priv->input, fifo, cnt); + } while (--fifo_cnt > 0); +} + +/** + * Check the keyboard controller and emit ASCII characters for any keys that + * are pressed. + * + * @param priv Keyboard private data + */ +static void check_for_keys(struct tegra_kbd_priv *priv) +{ + int fifo_cnt; + + if (!priv->first_scan && + get_timer(priv->last_poll_ms) < KBC_REPEAT_RATE_MS) + return; + priv->last_poll_ms = get_timer(0); + priv->first_scan = 0; + + /* + * Once we get here we know the keyboard has been scanned. So if there + * scan waiting for us, we know that nothing is held down. + */ + fifo_cnt = (readl(&priv->kbc->interrupt) >> 4) & 0xf; + process_fifo(priv, fifo_cnt); +} + +/** + * In order to detect keys pressed on boot, wait for the hardware to + * complete scanning the keys. This includes time to transition from + * Wkup mode to Continous polling mode and the repoll time. We can + * deduct the time that's already elapsed. + * + * @param priv Keyboard private data + */ +static void kbd_wait_for_fifo_init(struct tegra_kbd_priv *priv) +{ + if (!priv->inited) { + unsigned long elapsed_time; + long delay_ms; + + elapsed_time = get_timer(priv->start_time_ms); + delay_ms = priv->init_dly_ms - elapsed_time; + if (delay_ms > 0) { + udelay(delay_ms * 1000); + debug("%s: delay %ldms\n", __func__, delay_ms); + } + + priv->inited = 1; + } +} + +/** + * Check the tegra keyboard, and send any keys that are pressed. + * + * This is called by input_tstc() and input_getc() when they need more + * characters + * + * @param input Input configuration + * @return 1, to indicate that we have something to look at + */ +static int tegra_kbc_check(struct input_config *input) +{ + struct tegra_kbd_priv *priv = dev_get_priv(input->dev); + + kbd_wait_for_fifo_init(priv); + check_for_keys(priv); + + return 1; +} + +/* configures keyboard GPIO registers to use the rows and columns */ +static void config_kbc_gpio(struct tegra_kbd_priv *priv, struct kbc_tegra *kbc) +{ + int i; + + for (i = 0; i < KBC_MAX_GPIO; i++) { + u32 row_cfg, col_cfg; + u32 r_shift = 5 * (i % 6); + u32 c_shift = 4 * (i % 8); + u32 r_mask = 0x1f << r_shift; + u32 c_mask = 0xf << c_shift; + u32 r_offs = i / 6; + u32 c_offs = i / 8; + + row_cfg = readl(&kbc->row_cfg[r_offs]); + col_cfg = readl(&kbc->col_cfg[c_offs]); + + row_cfg &= ~r_mask; + col_cfg &= ~c_mask; + + if (i < priv->matrix.num_rows) { + row_cfg |= ((i << 1) | 1) << r_shift; + } else { + col_cfg |= (((i - priv->matrix.num_rows) << 1) | 1) + << c_shift; + } + + writel(row_cfg, &kbc->row_cfg[r_offs]); + writel(col_cfg, &kbc->col_cfg[c_offs]); + } +} + +/** + * Start up the keyboard device + */ +static void tegra_kbc_open(struct tegra_kbd_priv *priv) +{ + struct kbc_tegra *kbc = priv->kbc; + unsigned int scan_period; + u32 val; + + /* + * We will scan at twice the keyboard repeat rate, so that there is + * always a scan ready when we check it in check_for_keys(). + */ + scan_period = KBC_REPEAT_RATE_MS / 2; + writel(scan_period * KBC_CLOCK_KHZ, &kbc->rpt_dly); + writel(scan_period * KBC_CLOCK_KHZ, &kbc->init_dly); + /* + * Before reading from the keyboard we must wait for the init_dly + * plus the rpt_delay, plus 2ms for the row scan time. + */ + priv->init_dly_ms = scan_period * 2 + 2; + + val = KBC_DEBOUNCE_COUNT << KBC_DEBOUNCE_CNT_SHIFT; + val |= 1 << KBC_FIFO_TH_CNT_SHIFT; /* fifo interrupt threshold */ + val |= KBC_CONTROL_KBC_EN; /* enable */ + writel(val, &kbc->control); + + priv->start_time_ms = get_timer(0); + priv->last_poll_ms = get_timer(0); + priv->next_repeat_ms = priv->last_poll_ms; + priv->first_scan = 1; +} + +static int tegra_kbd_start(struct udevice *dev) +{ + struct tegra_kbd_priv *priv = dev_get_priv(dev); + + /* Set up pin mux and enable the clock */ + funcmux_select(PERIPH_ID_KBC, FUNCMUX_DEFAULT); + clock_enable(PERIPH_ID_KBC); + config_kbc_gpio(priv, priv->kbc); + + tegra_kbc_open(priv); + debug("%s: Tegra keyboard ready\n", __func__); + + return 0; +} + +/** + * Set up the tegra keyboard. This is called by the stdio device handler + * + * We want to do this init when the keyboard is actually used rather than + * at start-up, since keyboard input may not currently be selected. + * + * Once the keyboard starts there will be a period during which we must + * wait for the keyboard to init. We do this only when a key is first + * read - see kbd_wait_for_fifo_init(). + * + * @return 0 if ok, -ve on error + */ +static int tegra_kbd_probe(struct udevice *dev) +{ + struct tegra_kbd_priv *priv = dev_get_priv(dev); + struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev); + struct stdio_dev *sdev = &uc_priv->sdev; + struct input_config *input = &uc_priv->input; + int ret; + + priv->kbc = dev_read_addr_ptr(dev); + if ((fdt_addr_t)priv->kbc == FDT_ADDR_T_NONE) { + debug("%s: No keyboard register found\n", __func__); + return -EINVAL; + } + input_set_delays(input, KBC_REPEAT_DELAY_MS, KBC_REPEAT_RATE_MS); + + /* Decode the keyboard matrix information (16 rows, 8 columns) */ + ret = key_matrix_init(&priv->matrix, 16, 8, 1); + if (ret) { + debug("%s: Could not init key matrix: %d\n", __func__, ret); + return ret; + } + ret = key_matrix_decode_fdt(dev, &priv->matrix); + if (ret) { + debug("%s: Could not decode key matrix from fdt: %d\n", + __func__, ret); + return ret; + } + input_add_tables(input, false); + if (priv->matrix.fn_keycode) { + ret = input_add_table(input, KEY_FN, -1, + priv->matrix.fn_keycode, + priv->matrix.key_count); + if (ret) { + debug("%s: input_add_table() failed\n", __func__); + return ret; + } + } + + /* Register the device. init_tegra_keyboard() will be called soon */ + priv->input = input; + input->dev = dev; + input->read_keys = tegra_kbc_check; + strcpy(sdev->name, "tegra-kbc"); + ret = input_stdio_register(sdev); + if (ret) { + debug("%s: input_stdio_register() failed\n", __func__); + return ret; + } + + return 0; +} + +static const struct keyboard_ops tegra_kbd_ops = { + .start = tegra_kbd_start, +}; + +static const struct udevice_id tegra_kbd_ids[] = { + { .compatible = "nvidia,tegra20-kbc" }, + { } +}; + +U_BOOT_DRIVER(tegra_kbd) = { + .name = "tegra_kbd", + .id = UCLASS_KEYBOARD, + .of_match = tegra_kbd_ids, + .probe = tegra_kbd_probe, + .ops = &tegra_kbd_ops, + .priv_auto = sizeof(struct tegra_kbd_priv), +}; diff --git a/roms/u-boot/drivers/input/twl4030.c b/roms/u-boot/drivers/input/twl4030.c new file mode 100644 index 000000000..3a1c46b30 --- /dev/null +++ b/roms/u-boot/drivers/input/twl4030.c @@ -0,0 +1,87 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TWL4030 input + * + * Copyright (C) 2015 Paul Kocialkowski <contact@paulk.fr> + */ + +#include <twl4030.h> + +int twl4030_input_power_button(void) +{ + u8 data; + + twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_STS_HW_CONDITIONS, &data); + + if (data & TWL4030_PM_MASTER_STS_HW_CONDITIONS_PWON) + return 1; + + return 0; +} + +int twl4030_input_charger(void) +{ + u8 data; + + twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_STS_HW_CONDITIONS, &data); + + if (data & TWL4030_PM_MASTER_STS_HW_CONDITIONS_CHG) + return 1; + + return 0; +} + +int twl4030_input_usb(void) +{ + u8 data; + + twl4030_i2c_read_u8(TWL4030_CHIP_PM_MASTER, + TWL4030_PM_MASTER_STS_HW_CONDITIONS, &data); + + if (data & TWL4030_PM_MASTER_STS_HW_CONDITIONS_USB || + data & TWL4030_PM_MASTER_STS_HW_CONDITIONS_VBUS) + return 1; + + return 0; +} + +int twl4030_keypad_scan(unsigned char *matrix) +{ + u8 data; + u8 c, r; + + twl4030_i2c_read_u8(TWL4030_CHIP_KEYPAD, + TWL4030_KEYPAD_KEYP_CTRL_REG, &data); + + data |= TWL4030_KEYPAD_CTRL_SOFT_NRST | TWL4030_KEYPAD_CTRL_KBD_ON; + data &= ~TWL4030_KEYPAD_CTRL_SOFTMODEN; + + twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, + TWL4030_KEYPAD_KEYP_CTRL_REG, data); + + for (c = 0; c < 8; c++) { + data = 0xff & ~(1 << c); + twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, + TWL4030_KEYPAD_KBC_REG, data); + + data = 0xff; + twl4030_i2c_read_u8(TWL4030_CHIP_KEYPAD, + TWL4030_KEYPAD_KBR_REG, &data); + + for (r = 0; r < 8; r++) + matrix[c * 8 + r] = !(data & (1 << r)); + } + + data = 0xff & ~(TWL4030_KEYPAD_CTRL_SOFT_NRST); + twl4030_i2c_write_u8(TWL4030_CHIP_KEYPAD, + TWL4030_KEYPAD_KEYP_CTRL_REG, data); + + return 0; +} + +int twl4030_keypad_key(unsigned char *matrix, u8 c, u8 r) +{ + return matrix[c * 8 + r]; +} diff --git a/roms/u-boot/drivers/input/twl6030.c b/roms/u-boot/drivers/input/twl6030.c new file mode 100644 index 000000000..76bd3488f --- /dev/null +++ b/roms/u-boot/drivers/input/twl6030.c @@ -0,0 +1,47 @@ +// SPDX-License-Identifier: GPL-2.0+ +/* + * TWL6030 input + * + * Copyright (C) 2016 Paul Kocialkowski <contact@paulk.fr> + */ + +#include <twl6030.h> + +int twl6030_input_power_button(void) +{ + u8 value; + + twl6030_i2c_read_u8(TWL6030_CHIP_PM, TWL6030_STS_HW_CONDITIONS, &value); + + /* Power button is active low. */ + if (value & TWL6030_STS_HW_CONDITIONS_PWRON) + return 0; + + return 1; +} + +int twl6030_input_charger(void) +{ + u8 value; + + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, TWL6030_CONTROLLER_STAT1, + &value); + + if (value & TWL6030_CONTROLLER_STAT1_VAC_DET) + return 1; + + return 0; +} + +int twl6030_input_usb(void) +{ + u8 value; + + twl6030_i2c_read_u8(TWL6030_CHIP_CHARGER, TWL6030_CONTROLLER_STAT1, + &value); + + if (value & TWL6030_CONTROLLER_STAT1_VBUS_DET) + return 1; + + return 0; +} |