GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)
This commit is contained in:
@@ -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"
|
||||
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"
|
||||
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
|
||||
@@ -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
|
||||
@@ -0,0 +1,234 @@
|
||||
// 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 <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 mbkp_keyscan scan;
|
||||
unsigned int row, col, bit, data;
|
||||
int num_keys;
|
||||
|
||||
if (cros_ec_scan_keyboard(dev->parent, &scan)) {
|
||||
debug("%s: keyboard scan failed\n", __func__);
|
||||
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(cros_ec_kbd) = {
|
||||
.name = "cros_ec_kbd",
|
||||
.id = UCLASS_KEYBOARD,
|
||||
.of_match = cros_ec_kbd_ids,
|
||||
.probe = cros_ec_kbd_probe,
|
||||
.ops = &cros_ec_kbd_ops,
|
||||
.priv_auto_alloc_size = sizeof(struct cros_ec_keyb_priv),
|
||||
};
|
||||
@@ -0,0 +1,359 @@
|
||||
// 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 <asm/io.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_alloc_size = sizeof(struct i8042_kbd_priv),
|
||||
};
|
||||
@@ -0,0 +1,669 @@
|
||||
// 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 <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) },
|
||||
{},
|
||||
};
|
||||
|
||||
/*
|
||||
* Scan key code to ANSI 3.64 escape sequence table. 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, "\033[A"},
|
||||
{ KEY_DOWN, "\033[B"},
|
||||
{ KEY_RIGHT, "\033[C"},
|
||||
{ KEY_LEFT, "\033[D"},
|
||||
};
|
||||
|
||||
/* Maximum number of output characters that an ANSI sequence expands to */
|
||||
#define ANSI_CHAR_MAX 3
|
||||
|
||||
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;
|
||||
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;
|
||||
|
||||
/*
|
||||
* 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;
|
||||
} else {
|
||||
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;
|
||||
}
|
||||
@@ -0,0 +1,189 @@
|
||||
// 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 <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;
|
||||
}
|
||||
@@ -0,0 +1,90 @@
|
||||
// SPDX-License-Identifier: GPL-2.0+
|
||||
/*
|
||||
* Copyright (c) 2015 Google, Inc
|
||||
*/
|
||||
|
||||
#include <common.h>
|
||||
#include <dm.h>
|
||||
#include <keyboard.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_alloc_size = sizeof(struct keyboard_priv),
|
||||
};
|
||||
@@ -0,0 +1,352 @@
|
||||
// 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 <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/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 = (struct kbc_tegra *)devfdt_get_addr(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_alloc_size = sizeof(struct tegra_kbd_priv),
|
||||
};
|
||||
@@ -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];
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
Reference in New Issue
Block a user