aboutsummaryrefslogtreecommitdiffstats
path: root/roms/u-boot/drivers/input/cros_ec_keyb.c
diff options
context:
space:
mode:
authorAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
committerAngelos Mouzakitis <a.mouzakitis@virtualopensystems.com>2023-10-10 14:33:42 +0000
commitaf1a266670d040d2f4083ff309d732d648afba2a (patch)
tree2fc46203448ddcc6f81546d379abfaeb323575e9 /roms/u-boot/drivers/input/cros_ec_keyb.c
parente02cda008591317b1625707ff8e115a4841aa889 (diff)
Add submodule dependency filesHEADmaster
Change-Id: Iaf8d18082d3991dec7c0ebbea540f092188eb4ec
Diffstat (limited to 'roms/u-boot/drivers/input/cros_ec_keyb.c')
-rw-r--r--roms/u-boot/drivers/input/cros_ec_keyb.c255
1 files changed, 255 insertions, 0 deletions
diff --git a/roms/u-boot/drivers/input/cros_ec_keyb.c b/roms/u-boot/drivers/input/cros_ec_keyb.c
new file mode 100644
index 000000000..dc3b08c0f
--- /dev/null
+++ b/roms/u-boot/drivers/input/cros_ec_keyb.c
@@ -0,0 +1,255 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * Chromium OS Matrix Keyboard
+ *
+ * Copyright (c) 2012 The Chromium OS Authors.
+ */
+
+#include <common.h>
+#include <cros_ec.h>
+#include <dm.h>
+#include <errno.h>
+#include <input.h>
+#include <keyboard.h>
+#include <key_matrix.h>
+#include <log.h>
+#include <stdio_dev.h>
+
+enum {
+ KBC_MAX_KEYS = 8, /* Maximum keys held down at once */
+ KBC_REPEAT_RATE_MS = 30,
+ KBC_REPEAT_DELAY_MS = 240,
+};
+
+struct cros_ec_keyb_priv {
+ struct input_config *input; /* The input layer */
+ struct key_matrix matrix; /* The key matrix layer */
+ int key_rows; /* Number of keyboard rows */
+ int key_cols; /* Number of keyboard columns */
+ int ghost_filter; /* 1 to enable ghost filter, else 0 */
+};
+
+
+/**
+ * Check the keyboard controller and return a list of key matrix positions
+ * for which a key is pressed
+ *
+ * @param dev Keyboard device
+ * @param keys List of keys that we have detected
+ * @param max_count Maximum number of keys to return
+ * @param samep Set to true if this scan repeats the last, else false
+ * @return number of pressed keys, 0 for none, -EIO on error
+ */
+static int check_for_keys(struct udevice *dev, struct key_matrix_key *keys,
+ int max_count, bool *samep)
+{
+ struct cros_ec_keyb_priv *priv = dev_get_priv(dev);
+ struct key_matrix_key *key;
+ static struct mbkp_keyscan last_scan;
+ static bool last_scan_valid;
+ struct ec_response_get_next_event event;
+ struct mbkp_keyscan *scan = (struct mbkp_keyscan *)
+ &event.data.key_matrix;
+ unsigned int row, col, bit, data;
+ int num_keys;
+ int ret;
+
+ /* Get pending MKBP event. It may not be a key matrix event. */
+ do {
+ ret = cros_ec_get_next_event(dev->parent, &event);
+ /* The EC has no events for us at this time. */
+ if (ret == -EC_RES_UNAVAILABLE)
+ return -EIO;
+ else if (ret)
+ break;
+ } while (event.event_type != EC_MKBP_EVENT_KEY_MATRIX);
+
+ /* Try the old command if the EC doesn't support the above. */
+ if (ret == -EC_RES_INVALID_COMMAND) {
+ if (cros_ec_scan_keyboard(dev->parent, scan)) {
+ debug("%s: keyboard scan failed\n", __func__);
+ return -EIO;
+ }
+ } else if (ret) {
+ debug("%s: Error getting next MKBP event. (%d)\n",
+ __func__, ret);
+ return -EIO;
+ }
+ *samep = last_scan_valid && !memcmp(&last_scan, scan, sizeof(*scan));
+
+ /*
+ * This is a bit odd. The EC has no way to tell us that it has run
+ * out of key scans. It just returns the same scan over and over
+ * again. So the only way to detect that we have run out is to detect
+ * that this scan is the same as the last.
+ */
+ last_scan_valid = true;
+ memcpy(&last_scan, scan, sizeof(last_scan));
+
+ for (col = num_keys = bit = 0; col < priv->matrix.num_cols;
+ col++) {
+ for (row = 0; row < priv->matrix.num_rows; row++) {
+ unsigned int mask = 1 << (bit & 7);
+
+ data = scan->data[bit / 8];
+ if ((data & mask) && num_keys < max_count) {
+ key = keys + num_keys++;
+ key->row = row;
+ key->col = col;
+ key->valid = 1;
+ }
+ bit++;
+ }
+ }
+
+ return num_keys;
+}
+
+/**
+ * Check the keyboard, and send any keys that are pressed.
+ *
+ * This is called by input_tstc() and input_getc() when they need more
+ * characters
+ *
+ * @param input Input configuration
+ * @return 1, to indicate that we have something to look at
+ */
+int cros_ec_kbc_check(struct input_config *input)
+{
+ struct udevice *dev = input->dev;
+ struct cros_ec_keyb_priv *priv = dev_get_priv(dev);
+ static struct key_matrix_key last_keys[KBC_MAX_KEYS];
+ static int last_num_keys;
+ struct key_matrix_key keys[KBC_MAX_KEYS];
+ int keycodes[KBC_MAX_KEYS];
+ int num_keys, num_keycodes;
+ int irq_pending, sent;
+ bool same = false;
+
+ /*
+ * Loop until the EC has no more keyscan records, or we have
+ * received at least one character. This means we know that tstc()
+ * will always return non-zero if keys have been pressed.
+ *
+ * Without this loop, a key release (which generates no new ascii
+ * characters) will cause us to exit this function, and just tstc()
+ * may return 0 before all keys have been read from the EC.
+ */
+ do {
+ irq_pending = cros_ec_interrupt_pending(dev->parent);
+ if (irq_pending) {
+ num_keys = check_for_keys(dev, keys, KBC_MAX_KEYS,
+ &same);
+ if (num_keys < 0)
+ return 0;
+ last_num_keys = num_keys;
+ memcpy(last_keys, keys, sizeof(keys));
+ } else {
+ /*
+ * EC doesn't want to be asked, so use keys from last
+ * time.
+ */
+ num_keys = last_num_keys;
+ memcpy(keys, last_keys, sizeof(keys));
+ }
+
+ if (num_keys < 0)
+ return -1;
+ num_keycodes = key_matrix_decode(&priv->matrix, keys,
+ num_keys, keycodes, KBC_MAX_KEYS);
+ sent = input_send_keycodes(input, keycodes, num_keycodes);
+
+ /*
+ * For those ECs without an interrupt, stop scanning when we
+ * see that the scan is the same as last time.
+ */
+ if ((irq_pending < 0) && same)
+ break;
+ } while (irq_pending && !sent);
+
+ return 1;
+}
+
+/**
+ * Decode MBKP keyboard details from the device tree
+ *
+ * @param blob Device tree blob
+ * @param node Node to decode from
+ * @param config Configuration data read from fdt
+ * @return 0 if ok, -1 on error
+ */
+static int cros_ec_keyb_decode_fdt(struct udevice *dev,
+ struct cros_ec_keyb_priv *config)
+{
+ /*
+ * Get keyboard rows and columns - at present we are limited to
+ * 8 columns by the protocol (one byte per row scan)
+ */
+ config->key_rows = dev_read_u32_default(dev, "keypad,num-rows", 0);
+ config->key_cols = dev_read_u32_default(dev, "keypad,num-columns", 0);
+ if (!config->key_rows || !config->key_cols ||
+ config->key_rows * config->key_cols / 8
+ > CROS_EC_KEYSCAN_COLS) {
+ debug("%s: Invalid key matrix size %d x %d\n", __func__,
+ config->key_rows, config->key_cols);
+ return -1;
+ }
+ config->ghost_filter = dev_read_bool(dev, "google,needs-ghost-filter");
+
+ return 0;
+}
+
+static int cros_ec_kbd_probe(struct udevice *dev)
+{
+ struct cros_ec_keyb_priv *priv = dev_get_priv(dev);
+ struct keyboard_priv *uc_priv = dev_get_uclass_priv(dev);
+ struct stdio_dev *sdev = &uc_priv->sdev;
+ struct input_config *input = &uc_priv->input;
+ int ret;
+
+ ret = cros_ec_keyb_decode_fdt(dev, priv);
+ if (ret) {
+ debug("%s: Cannot decode node (ret=%d)\n", __func__, ret);
+ return -EINVAL;
+ }
+ input_set_delays(input, KBC_REPEAT_DELAY_MS, KBC_REPEAT_RATE_MS);
+ ret = key_matrix_init(&priv->matrix, priv->key_rows, priv->key_cols,
+ priv->ghost_filter);
+ if (ret) {
+ debug("%s: cannot init key matrix\n", __func__);
+ return ret;
+ }
+ ret = key_matrix_decode_fdt(dev, &priv->matrix);
+ if (ret) {
+ debug("%s: Could not decode key matrix from fdt\n", __func__);
+ return ret;
+ }
+ debug("%s: Matrix keyboard %dx%d ready\n", __func__, priv->key_rows,
+ priv->key_cols);
+
+ priv->input = input;
+ input->dev = dev;
+ input_add_tables(input, false);
+ input->read_keys = cros_ec_kbc_check;
+ strcpy(sdev->name, "cros-ec-keyb");
+
+ /* Register the device. cros_ec_init_keyboard() will be called soon */
+ return input_stdio_register(sdev);
+}
+
+static const struct keyboard_ops cros_ec_kbd_ops = {
+};
+
+static const struct udevice_id cros_ec_kbd_ids[] = {
+ { .compatible = "google,cros-ec-keyb" },
+ { }
+};
+
+U_BOOT_DRIVER(google_cros_ec_keyb) = {
+ .name = "google_cros_ec_keyb",
+ .id = UCLASS_KEYBOARD,
+ .of_match = cros_ec_kbd_ids,
+ .probe = cros_ec_kbd_probe,
+ .ops = &cros_ec_kbd_ops,
+ .priv_auto = sizeof(struct cros_ec_keyb_priv),
+};