GK SDK 源码库: XMIPCLinuxV100R005C00SPC030 (kernel/tools/open_source excluded)

This commit is contained in:
lai
2026-09-06 03:52:57 +08:00
commit b1928b41c0
21813 changed files with 4413081 additions and 0 deletions
@@ -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;
}