@@ -26,6 +26,7 @@ include $(TOPDIR)/config.mk
LIB := $(obj)libinput.o
COBJS-$(CONFIG_I8042_KBD) += i8042.o
+COBJS-$(CONFIG_TEGRA2_KEYBOARD) += tegra-kbc.o
ifdef CONFIG_PS2KBD
COBJS-y += keyboard.o pc_keyb.o
COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o
new file mode 100644
@@ -0,0 +1,626 @@
+/*
+ * (C) Copyright 2011
+ * NVIDIA Corporation <www.nvidia.com>
+ *
+ * See file CREDITS for list of people who contributed to this
+ * project.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License as
+ * published by the Free Software Foundation; either version 2 of
+ * the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston,
+ * MA 02111-1307 USA
+ */
+#define DEBUG
+#include <common.h>
+#include <malloc.h>
+#include <stdio_dev.h>
+#include <asm/io.h>
+#include <asm/arch/clock.h>
+#include <asm/arch/pinmux.h>
+#include <asm/arch/timer.h>
+#include <tegra-kbc.h>
+#include <fdtdec.h>
+
+DECLARE_GLOBAL_DATA_PTR;
+
+#define DEVNAME "tegra-kbc"
+
+enum {
+ KBC_MAX_ROW = 16,
+ KBC_MAX_COL = 8,
+ KBC_KEY_COUNT = KBC_MAX_ROW * KBC_MAX_COL,
+ KBC_MAX_GPIO = 24,
+ KBC_MAX_KPENT = 8, /* size of keypress entry queue */
+};
+
+enum KEYS {
+ KEY_FIRST_MODIFIER = 220,
+ KEY_RIGHT_CTRL = KEY_FIRST_MODIFIER,
+ KEY_LEFT_CTRL,
+ KEY_FN,
+ KEY_SHIFT,
+};
+
+/* KBC row scan time and delay for beginning the row scan (in usecs) */
+#define KBC_ROW_SCAN_TIME 16
+#define KBC_ROW_SCAN_DLY 5
+
+/* uses a 32KHz clock so a cycle = 1/32Khz */
+#define KBC_CYCLE_IN_USEC DIV_ROUND_UP(1000, 32)
+
+#define KBC_FIFO_TH_CNT_SHIFT(cnt) (cnt << 14)
+#define KBC_DEBOUNCE_CNT_SHIFT(cnt) (cnt << 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_RPT_DLY 20
+#define KBC_RPT_RATE 4
+
+/*
+ * Use a simple FIFO to convert some keys into escape sequences and to handle
+ * testc vs getc. The FIFO length must be a power of two. Currently, four
+ * characters is the smallest power of two that is large enough to contain all
+ * generated escape sequences.
+ */
+#define KBC_FIFO_LENGTH (1 << 2)
+
+static struct keyb {
+ /*
+ * Information about keycode mappings. The plain_keycode array must
+ * exist but the others may be NULL in which case they are not
+ * decoded.
+ */
+ const u8 *plain_keycode; /* key code for each row / column */
+ const u8 *shift_keycode; /* ...when shift held down */
+ const u8 *fn_keycode; /* ...when Fn held down */
+ const u8 *ctrl_keycode; /* ...when ctrl held down */
+
+ struct kbc_tegra *kbc; /* tegra keyboard controller */
+ int prev_fifo[KBC_MAX_KPENT]; /* keys left over from last scan */
+ int prev_cnt; /* number of keys left over */
+ unsigned char prev_key; /* previously decoded key */
+ unsigned int rpt_dly; /* delay until next key repeat */
+ unsigned char repeat_key; /* the key being held down */
+ unsigned char inited; /* 1 if keyboard has been inited */
+ int fifo[KBC_FIFO_LENGTH]; /* key fifo, with decoded keys */
+ int fifo_read; /* Key fifo read pointer */
+ int fifo_write; /* Key fifo write pointer */
+ unsigned int repoll_time; /* next time to poll keyboard (us) */
+
+ /*
+ * 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; /* Delay before we can read keyboard */
+ unsigned long start_time; /* Time that we inited (in us) */
+} config;
+
+
+static int tegra_kbc_keycode(struct keyb *config, int r, int c,
+ int modifier)
+{
+ int entry = r * KBC_MAX_COL + c;
+
+ if (modifier == KEY_FN && config->fn_keycode)
+ return config->fn_keycode[entry];
+
+ /* Put ctrl keys ahead of shift */
+ else if ((modifier == KEY_LEFT_CTRL || modifier == KEY_RIGHT_CTRL)
+ && config->ctrl_keycode)
+ return config->ctrl_keycode[entry];
+ else if (modifier == KEY_SHIFT && config->shift_keycode)
+ return config->shift_keycode[entry];
+ else
+ return config->plain_keycode[entry];
+}
+
+/* determines if current keypress configuration can cause key ghosting */
+static int is_ghost_key_config(int *rows_val, int *cols_val, int valid)
+{
+ int i, j, key_in_same_col = 0, key_in_same_row = 0;
+
+ /*
+ * Matrix keyboard designs are prone to keyboard ghosting.
+ * Ghosting occurs if there are 3 keys such that -
+ * any 2 of the 3 keys share a row, and any 2 of them share a column.
+ */
+ 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 (cols_val[j] == cols_val[i])
+ key_in_same_col = 1;
+ if (rows_val[j] == rows_val[i])
+ key_in_same_row = 1;
+ }
+ }
+
+ if (key_in_same_col && key_in_same_row)
+ return 1;
+ else
+ return 0;
+}
+
+/* reads the keyboard fifo for current keypresses. */
+static int tegra_kbc_find_keys(struct keyb *config, int *fifo)
+{
+ int rows_val[KBC_MAX_KPENT], cols_val[KBC_MAX_KPENT];
+ u32 kp_ent_val[(KBC_MAX_KPENT + 3) / 4];
+ u32 *kp_ents = kp_ent_val;
+ u32 kp_ent = 0;
+ int i, j, k, valid = 0;
+ int modifier = 0;
+
+ for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++)
+ kp_ent_val[i] = readl(&config->kbc->kp_ent[i]);
+
+ valid = 0;
+ for (i = 0; i < KBC_MAX_KPENT; i++) {
+ if (!(i&3))
+ kp_ent = *kp_ents++;
+
+ if (kp_ent & KBC_KPENT_VALID) {
+ cols_val[valid] = kp_ent & 0x7;
+ rows_val[valid++] = (kp_ent >> 3) & 0xf;
+ }
+ kp_ent >>= 8;
+ }
+ for (i = 0; i < valid; i++) {
+ k = tegra_kbc_keycode(config, rows_val[i], cols_val[i], 0);
+ if (KEY_IS_MODIFIER(k)) {
+ modifier = k;
+ break;
+ }
+ }
+
+ /* For a ghost key config, ignore the keypresses for this iteration. */
+ if ((valid >= 3) && is_ghost_key_config(rows_val, cols_val, valid))
+ return KBC_MAX_KPENT + 1;
+
+ j = 0;
+ for (i = 0; i < valid; i++) {
+ k = tegra_kbc_keycode(config, rows_val[i], cols_val[i],
+ modifier);
+ /* Key modifiers (Fn and Shift) will not be part of the fifo */
+ if (k && (k != -1))
+ fifo[j++] = k;
+ }
+
+ return j;
+}
+
+static void process_keys(struct keyb *config, int fifo[], int cnt)
+{
+ int prev_key_in_fifo = 0;
+ char key = config->prev_key;
+ int i, j;
+
+ /*
+ * If multiple keys are pressed such that it results in * 2 or more
+ * unmodified keys, we will determine the newest key and report
+ * that to the upper layer.
+ */
+ for (i = 0; i < cnt; i++) {
+ for (j = 0; j < config->prev_cnt; j++) {
+ if (fifo[i] == config->prev_fifo[j])
+ break;
+ }
+ /* Found a new key. */
+ if (j == config->prev_cnt) {
+ key = fifo[i];
+ break;
+ }
+ if (config->prev_key == fifo[i])
+ prev_key_in_fifo = 1;
+ }
+ /*
+ * Keys were released and FIFO does not contain * the previous
+ * reported key. So report a null key.
+ */
+ if (i == cnt && !prev_key_in_fifo)
+ key = 0;
+
+ for (i = 0; i < cnt; i++)
+ config->prev_fifo[i] = fifo[i];
+ config->prev_cnt = cnt;
+ config->prev_key = key;
+}
+
+/* processes all the keypresses in fifo and returns a single key */
+static int tegra_kbc_get_single_char(struct keyb *config, u32 fifo_cnt)
+{
+ int fifo[KBC_MAX_KPENT] = {0};
+ int cnt;
+
+ if (fifo_cnt) {
+ do {
+ cnt = tegra_kbc_find_keys(config, fifo);
+ /* If this is a ghost key combo, report no change */
+ if (cnt <= KBC_MAX_KPENT)
+ process_keys(config, fifo, cnt);
+ } while (!config->prev_key && --fifo_cnt);
+ } else {
+ config->prev_cnt = 0;
+ config->prev_key = 0;
+ }
+
+ udelay((fifo_cnt == 1) ? config->repoll_time : 1000);
+
+ return config->prev_key;
+}
+
+/* manages keyboard hardware registers on keypresses and returns a key.*/
+static unsigned char tegra_kbc_get_char(struct keyb *config)
+{
+ struct kbc_tegra *kbc = config->kbc;
+ u32 val, ctl;
+ char key = 0;
+
+ /*
+ * Until all keys are released, defer further processing to
+ * the polling loop.
+ */
+ ctl = readl(&kbc->control);
+ ctl &= ~KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(ctl, &kbc->control);
+
+ /*
+ * Quickly bail out & reenable interrupts if the interrupt source
+ * wasn't fifo count threshold
+ */
+ val = readl(&kbc->interrupt);
+ writel(val, &kbc->interrupt);
+
+ if (val & KBC_INT_FIFO_CNT_INT_STATUS)
+ key = tegra_kbc_get_single_char(config, (val >> 4) & 0xf);
+
+ ctl |= KBC_CONTROL_FIFO_CNT_INT_EN;
+ writel(ctl, &kbc->control);
+
+ return key;
+}
+
+/* handles key repeat delay and key repeat rate.*/
+static int kbd_fetch_char(struct keyb *config, int test)
+{
+ unsigned char key;
+
+ do {
+ key = tegra_kbc_get_char(config);
+ if (!key) {
+ config->repeat_key = 0;
+ if (test)
+ break;
+ else
+ continue;
+ }
+
+ /* This logic takes care of the repeat rate */
+ if ((key != config->repeat_key) || !(config->rpt_dly--))
+ break;
+ } while (1);
+
+ if (key == config->repeat_key) {
+ config->rpt_dly = KBC_RPT_RATE;
+ } else {
+ config->rpt_dly = KBC_RPT_DLY;
+ config->repeat_key = key;
+ }
+
+ return key;
+}
+
+/*
+ * Return true if there are no characters in the FIFO.
+ */
+static int kbd_fifo_empty(struct keyb *config)
+{
+ return config->fifo_read == config->fifo_write;
+}
+
+/*
+ * Return number of characters of free space in the FIFO.
+ */
+static int kbd_fifo_free_space(struct keyb *config)
+{
+ return KBC_FIFO_LENGTH - (config->fifo_write - config->fifo_read);
+}
+
+/*
+ * Insert a character into the FIFO. Calling this function when the FIFO is
+ * full will overwrite the oldest character in the FIFO.
+ */
+static void kbd_fifo_insert(struct keyb *config, int key)
+{
+ int index = config->fifo_write & (KBC_FIFO_LENGTH - 1);
+
+ assert(kbd_fifo_free_space(config) > 0);
+
+ config->fifo[index] = key;
+
+ config->fifo_write++;
+}
+
+/*
+ * Remove a character from the FIFO, it is an error to call this function when
+ * the FIFO is empty.
+ */
+static int kbd_fifo_remove(struct keyb *config)
+{
+ int index = config->fifo_read & (KBC_FIFO_LENGTH - 1);
+ int key = config->fifo[index];
+
+ assert(!kbd_fifo_empty(config));
+
+ config->fifo_read++;
+
+ return key;
+}
+
+/*
+ * Given a keycode, convert it to the sequence of characters that U-Boot expects
+ * to recieve from its input devices and insert the characters into the FIFO.
+ * If the keycode is 0, ignore it. Currently this function will only ever add
+ * at most three characters to the FIFO, so it is always safe to call when the
+ * FIFO is empty.
+ */
+static void kbd_fifo_refill(struct keyb *config, int key)
+{
+ switch (key) {
+ /*
+ * We need to deal with a zero keycode value here because the
+ * testc call can call us with zero if no key is pressed.
+ */
+ case 0x00:
+ break;
+
+ /*
+ * Generate escape sequences for arrow keys. Additional escape
+ * sequences can be added to the switch statements, but it may
+ * be better in the future t0 use the top bit of the keycode to
+ * indicate a key that generates an escape sequence. Then the
+ * outer switch could turn into an "if (key & 0x80)".
+ */
+ case 0x1C:
+ case 0x1D:
+ case 0x1E:
+ case 0x1F:
+ kbd_fifo_insert(config, 0x1B); /* Escape */
+ kbd_fifo_insert(config, '[');
+
+ switch (key) {
+ case 0x1C:
+ kbd_fifo_insert(config, 'D');
+ break;
+ case 0x1D:
+ kbd_fifo_insert(config, 'C');
+ break;
+ case 0x1E:
+ kbd_fifo_insert(config, 'A');
+ break;
+ case 0x1F:
+ kbd_fifo_insert(config, 'B');
+ break;
+ }
+ break;
+
+ /*
+ * All other keycodes can be treated as characters as is and
+ * are inserted into the FIFO directly.
+ */
+ default:
+ kbd_fifo_insert(config, key);
+ break;
+ }
+}
+
+static void kbd_wait_for_fifo_init(struct keyb *config)
+{
+ /*
+ * 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 thats already elapsed.
+ */
+ if (!config->inited) {
+ unsigned long elapsed_time;
+ long delay;
+
+ elapsed_time = timer_get_us() - config->start_time;
+ delay = config->init_dly + config->repoll_time - elapsed_time;
+ if (delay > 0)
+ udelay(delay);
+
+ config->inited = 1;
+ }
+}
+
+static int kbd_testc(void)
+{
+ kbd_wait_for_fifo_init(&config);
+
+ if (kbd_fifo_empty(&config))
+ kbd_fifo_refill(&config, kbd_fetch_char(&config, 1));
+
+ return !kbd_fifo_empty(&config);
+}
+
+static int kbd_getc(void)
+{
+ if (kbd_fifo_empty(&config))
+ kbd_fifo_refill(&config, kbd_fetch_char(&config, 0));
+
+ return kbd_fifo_remove(&config);
+}
+
+/* configures keyboard GPIO registers to use the rows and columns */
+static void config_kbc(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 < KBC_MAX_ROW)
+ row_cfg |= ((i << 1) | 1) << r_shift;
+ else
+ col_cfg |= (((i - KBC_MAX_ROW) << 1) | 1) << c_shift;
+
+ writel(row_cfg, &kbc->row_cfg[r_offs]);
+ writel(col_cfg, &kbc->col_cfg[c_offs]);
+ }
+}
+
+static int tegra_kbc_open(void)
+{
+ unsigned int scan_time_rows, debounce_cnt, rpt_cnt;
+ struct kbc_tegra *kbc = config.kbc;
+ u32 val = 0;
+
+ config_kbc(kbc);
+
+ /*
+ * The time delay between two consecutive reads of the FIFO is
+ * the sum of the repeat time and the time taken for scanning
+ * the rows. There is an additional delay before the row scanning
+ * starts. The repoll delay is computed in microseconds.
+ */
+ rpt_cnt = 5 * DIV_ROUND_UP(1000, KBC_CYCLE_IN_USEC);
+ debounce_cnt = 2;
+ scan_time_rows = (KBC_ROW_SCAN_TIME + debounce_cnt) * KBC_MAX_ROW;
+ config.repoll_time = KBC_ROW_SCAN_DLY + scan_time_rows + rpt_cnt;
+ config.repoll_time = config.repoll_time * KBC_CYCLE_IN_USEC;
+
+ writel(rpt_cnt, &kbc->rpt_dly);
+
+ val = KBC_DEBOUNCE_CNT_SHIFT(debounce_cnt);
+ val |= KBC_FIFO_TH_CNT_SHIFT(1); /* set fifo interrupt threshold to 1 */
+ val |= KBC_CONTROL_FIFO_CNT_INT_EN; /* interrupt on FIFO threshold */
+ val |= KBC_CONTROL_KBC_EN; /* enable */
+
+ writel(val, &kbc->control);
+
+ config.init_dly = readl(&kbc->init_dly) * KBC_CYCLE_IN_USEC;
+ config.start_time = timer_get_us();
+
+ return 0;
+}
+
+void config_kbc_pinmux(void)
+{
+ enum pmux_pingrp pingrp[] = {PINGRP_KBCA, PINGRP_KBCB, PINGRP_KBCC,
+ PINGRP_KBCD, PINGRP_KBCE, PINGRP_KBCF};
+ int i;
+
+ for (i = 0; i < (sizeof(pingrp)/sizeof(pingrp[0])); i++) {
+ pinmux_tristate_disable(pingrp[i]);
+ pinmux_set_func(pingrp[i], PMUX_FUNC_KBC);
+ pinmux_set_pullupdown(pingrp[i], PMUX_PULL_UP);
+ }
+}
+
+static int fdt_decode_kbc(const void *blob, int node, struct keyb *config)
+{
+ config->kbc = (struct kbc_tegra *)fdtdec_get_addr(blob, node, "reg");
+ config->plain_keycode = fdtdec_locate_byte_array(blob, node,
+ "keycode-plain", KBC_KEY_COUNT);
+ config->shift_keycode = fdtdec_locate_byte_array(blob, node,
+ "keycode-shift", KBC_KEY_COUNT);
+ config->fn_keycode = fdtdec_locate_byte_array(blob, node,
+ "keycode-fn", KBC_KEY_COUNT);
+ config->ctrl_keycode = fdtdec_locate_byte_array(blob, node,
+ "keycode-ctrl", KBC_KEY_COUNT);
+ if (!config->plain_keycode) {
+ debug("%s: cannot find keycode-plain map\n", __func__);
+ return -1;
+ }
+ return 0;
+}
+
+int drv_keyboard_init(void)
+{
+ int error;
+ struct stdio_dev kbddev;
+ char *stdinname;
+
+#ifdef CONFIG_OF_CONTROL
+ int node;
+
+ node = fdtdec_next_compatible(gd->fdt_blob, 0,
+ COMPAT_NVIDIA_TEGRA20_KBC);
+ if (node < 0) {
+ debug("%s: cannot locate keyboard node\n", __func__);
+ return node;
+ }
+ if (fdt_decode_kbc(gd->fdt_blob, node, &config))
+ return -1;
+#else
+#error "Tegra keyboard driver requires FDT definitions"
+#endif
+ config.rpt_dly = KBC_RPT_DLY;
+
+ config_kbc_pinmux();
+
+ /*
+ * All of the Tegra board use the same clock configuration for now.
+ * This can be moved to the board specific configuration if that
+ * changes.
+ */
+ clock_enable(PERIPH_ID_KBC);
+
+ stdinname = getenv("stdin");
+ memset(&kbddev, 0, sizeof(kbddev));
+ strcpy(kbddev.name, DEVNAME);
+ kbddev.flags = DEV_FLAGS_INPUT | DEV_FLAGS_SYSTEM;
+ kbddev.putc = NULL;
+ kbddev.puts = NULL;
+ kbddev.getc = kbd_getc;
+ kbddev.tstc = kbd_testc;
+ kbddev.start = tegra_kbc_open;
+
+ error = stdio_register(&kbddev);
+ if (!error) {
+ /* check if this is the standard input device */
+ if (strcmp(stdinname, DEVNAME) == 0) {
+ /* reassign the console */
+ if (OVERWRITE_CONSOLE)
+ return 1;
+
+ error = console_assign(stdin, DEVNAME);
+ if (!error)
+ return 0;
+ else
+ return error;
+ }
+ return 1;
+ }
+
+ return error;
+}
@@ -58,6 +58,7 @@ struct fdt_memory {
enum fdt_compat_id {
COMPAT_UNKNOWN,
COMPAT_NVIDIA_TEGRA20_USB, /* Tegra2 USB port */
+ COMPAT_NVIDIA_TEGRA20_KBC, /* Tegra2 Keyboard */
COMPAT_COUNT,
};
new file mode 100644
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 2011 The Chromium OS Authors. All rights reserved.
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef __include_tegra_kbc_h__
+#define __include_tegra_kbc_h__
+
+#include <common.h>
+
+#define KEY_IS_MODIFIER(key) ((key) >= KEY_FIRST_MODIFIER)
+
+struct kbc_tegra {
+ u32 control;
+ u32 interrupt;
+ u32 row_cfg[4];
+ u32 col_cfg[3];
+ u32 timeout_cnt;
+ u32 init_dly;
+ u32 rpt_dly;
+ u32 kp_ent[2];
+ u32 row_mask[16];
+};
+
+#ifdef CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE
+extern int overwrite_console(void);
+#define OVERWRITE_CONSOLE overwrite_console()
+#else
+#define OVERWRITE_CONSOLE 0
+#endif /* CONFIG_SYS_CONSOLE_OVERWRITE_ROUTINE */
+
+#endif /* __include_tegra_kbc_h__ */
@@ -38,6 +38,7 @@ DECLARE_GLOBAL_DATA_PTR;
static const char * const compat_names[COMPAT_COUNT] = {
COMPAT(UNKNOWN, "<none>"),
COMPAT(NVIDIA_TEGRA20_USB, "nvidia,tegra20-ehci"),
+ COMPAT(NVIDIA_TEGRA20_KBC, "nvidia,tegra20-kbc"),
};
/**