Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(561)

Unified Diff: drivers/input/tegra-kbc.c

Issue 3548006: Tegra2: input: Add tegra keyboard support. (Closed) Base URL: http://git.chromium.org/git/u-boot.git
Patch Set: Removing static variable's zero init Created 10 years, 2 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« board/tegra2/seaboard/seaboard.c ('K') | « drivers/input/Makefile ('k') | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: drivers/input/tegra-kbc.c
diff --git a/drivers/input/tegra-kbc.c b/drivers/input/tegra-kbc.c
new file mode 100755
index 0000000000000000000000000000000000000000..63ca0705a55e4bcaad260c13a63d22f954639a9c
--- /dev/null
+++ b/drivers/input/tegra-kbc.c
@@ -0,0 +1,412 @@
+/*
+ * (C) Copyright 2010
+ * 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
+ */
+
+#include <common.h>
+#include <malloc.h>
+#include <stdio_dev.h>
+
+#define DEVNAME "tegra-kbc"
+
+#define KBC_MAX_GPIO 24
+#define KBC_MAX_KPENT 8
+#define KBC_MAX_ROW 16
+#define KBC_MAX_COL 8
+
+#define KBC_MAX_KEY (KBC_MAX_ROW*KBC_MAX_COL)
+
+#define TEGRA_KBC_BASE 0x7000E200
+
+#define KBC_CONTROL_0 0
+#define KBC_INT_0 4
+#define KBC_ROW_CFG0_0 8
+#define KBC_COL_CFG0_0 0x18
+#define KBC_RPT_DLY_0 0x2c
+#define KBC_KP_ENT0_0 0x30
+#define KBC_KP_ENT1_0 0x34
+#define KBC_ROW0_MASK_0 0x38
+
+#define KBC_RPT_DLY 20
+#define KBC_RPT_RATE 4
+
+#define readl(addr) (*(volatile unsigned int *)(addr))
+#define writel(b, addr) ((*(volatile unsigned int *) (addr)) = (b))
+#define kbc_readl(addr) readl(TEGRA_KBC_BASE + addr)
+#define kbc_writel(b, addr) writel(b, TEGRA_KBC_BASE + addr)
+
+/* Define function and shift keys to untypable ASCII values */
+#define KEY_FN 222
+#define KEY_SHIFT 223
+
+#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 */
+
+extern void config_kbc_pinmux(void);
+extern void config_kbc_clock(void);
+
+enum bool {false = 0, true = 1};
+
+struct tegra_kbc {
+ void *mmio;
+ unsigned int repoll_time;
+ unsigned int debounce_cnt;
+ unsigned int rpt_cnt;
+ int *plain_keycode;
+ int *fn_keycode;
+ int *shift_keycode;
+};
+
+struct tegra_kbc *kbc;
+
+static int plain_kbd_keycode[] = {
+ 0, 0, 'w', 's', 'a', 'z', 0, KEY_FN,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '5', '4', 'r', 'e', 'f', 'd', 'x', 0,
+ '7', '6', 't', 'h', 'g', 'v', 'c', ' ',
+ '9', '8', 'u', 'y', 'j', 'n', 'b', '\\',
+ '-', '0', 'o', 'i', 'l', 'k', ',', 'm',
+ 0, '=', ']', '\r', 0, 0, 0, 0,
+ 0, 0, 0, 0, KEY_SHIFT, KEY_SHIFT, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '[', 'p', '\'', ';', '/', '.', 0, 0,
+ 0, 0, 0x08, '3', '2', 0, 0, 0,
+ 0, 0x7F, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 'q', 0, 0, '1', 0,
+ 0x1B, '`', 0, 0x9, 0, 0, 0, 0
+};
+
+static int shift_kbd_keycode[] = {
+ 0, 0, 'W', 'S', 'A', 'Z', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '%', '$', 'R', 'E', 'F', 'D', 'X', 0,
+ '&', '^', 'T', 'H', 'G', 'V', 'C', ' ',
+ '(', '*', 'U', 'Y', 'J', 'N', 'B', '|',
+ '_', ')', 'O', 'I', 'L', 'K', ',', 'M',
+ 0, '+', '}', '\r', 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '{', 'P', '"', ':', '?', '>', 0, 0,
+ 0, 0, 0x08, '#', '@', 0, 0, 0,
+ 0, 0x7F, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 'Q', 0, 0, '!', 0,
+ 0x1B, '~', 0, 0x9, 0, 0, 0, 0
+};
+
+static int fn_kbd_keycode[] = {
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ '7', 0, 0, 0, 0, 0, 0, 0,
+ '9', '8', '4', 0, '1', 0, 0, 0,
+ 0, '/', '6', '5', '3', '2', 0, '0',
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, '\'', 0, '-', '+', '.', 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, '?', 0, 0, 0
+};
+
+void msleep(int a)
+{
+ int i;
+
+ for (i = 0; i < a; i++)
+ udelay(1000);
+}
+
+static int tegra_kbc_keycode(struct tegra_kbc *kbc, int r, int c, int spl_key)
+{
+ if (spl_key == KEY_FN)
+ return kbc->fn_keycode[(r * KBC_MAX_COL) + c];
+ else if (spl_key == KEY_SHIFT)
+ return kbc->shift_keycode[(r * KBC_MAX_COL) + c];
+ else
+ return kbc->plain_keycode[(r * KBC_MAX_COL) + c];
+}
+
+static int tegra_kbc_find_keys(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, valid = 0;
+ int spl = 0;
+
+ for (i = 0; i < ARRAY_SIZE(kp_ent_val); i++)
+ kp_ent_val[i] = kbc_readl(KBC_KP_ENT0_0 + (i*4));
+
+ valid = 0;
+ for (i = 0; i < KBC_MAX_KPENT; i++) {
+ if (!(i&3))
+ kp_ent = *kp_ents++;
+
+ if (kp_ent & 0x80) {
+ cols_val[valid] = kp_ent & 0x7;
+ rows_val[valid++] = (kp_ent >> 3) & 0xf;
+ }
+ kp_ent >>= 8;
+ }
+
+ for (i = 0; i < valid; i++) {
+ int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], 0);
+ if ((k == KEY_FN) || (k == KEY_SHIFT)) {
+ spl = k;
+ break;
+ }
+ }
+
+ j = 0;
+ for (i = 0; i < valid; i++) {
+ int k = tegra_kbc_keycode(kbc, rows_val[i], cols_val[i], spl);
+ if (k != -1)
+ fifo[j++] = k;
+ }
+
+ return j;
+}
+
+static unsigned char tegra_kbc_get_char(void)
+{
+ u32 val, ctl;
+ int fifo[KBC_MAX_KPENT], i, cnt;
+ char c = 0;
+
+ for (i = 0; i < KBC_MAX_KPENT; i++)
+ fifo[i] = 0;
+
+ /* until all keys are released, defer further processing to
+ * the polling loop in tegra_kbc_key_repeat */
+ ctl = kbc_readl(KBC_CONTROL_0);
+ ctl &= ~(1<<3);
+ kbc_writel(ctl, KBC_CONTROL_0);
+
+ /* quickly bail out & reenable interrupts if the interrupt source
+ * wasn't fifo count threshold */
+ val = kbc_readl(KBC_INT_0);
+ kbc_writel(val, KBC_INT_0);
+
+ if (!(val & (1<<2))) {
+ ctl |= 1<<3;
+ kbc_writel(ctl, KBC_CONTROL_0);
+ return 0;
+ }
+
+ val = (val >> 4) & 0xf;
+ if (val) {
+ cnt = tegra_kbc_find_keys(fifo);
+
+ /*
+ * Get to the firxt non-zero key value in the key fifo.
+ * The FN and Shift keys will appear as zero values.
+ * The U-boot upper layers can accept only one key.
+ */
+ for (i = 0; i < cnt; i++) {
+ if (fifo[i]) {
+ c = fifo[i];
+ break;
+ }
+ }
+ }
+
+ msleep((val == 1) ? kbc->repoll_time : 1);
+
+ ctl |= (1<<3);
+ kbc_writel(ctl, KBC_CONTROL_0);
+
+ return c;
+}
+
+static int kbd_fetch_char(int loop)
+{
+ unsigned char c;
+ static unsigned char prev_c;
+ static unsigned int rpt_dly = KBC_RPT_DLY;
+
+ do {
+ c = tegra_kbc_get_char();
+ if (!c) {
+ prev_c = 0;
+ continue;
+ }
+
+ /* This logic takes care of the repeat rate */
+ if ((c != prev_c) || !(rpt_dly--))
+ break;
+ } while (loop);
+
+ if (c == prev_c) {
+ /* kbc_testc should return 0 to indicate repeat charachters */
+ if (!loop)
+ c = 0;
+ else
+ rpt_dly = KBC_RPT_RATE;
+ } else {
+ rpt_dly = KBC_RPT_DLY;
+ prev_c = c;
+ }
+
+ return c;
+}
+
+static int kbd_testc(void)
+{
+ unsigned char c = kbd_fetch_char(false);
+
+ return (c != 0);
+}
+
+static int kbd_getc(void)
+{
+ unsigned char c = kbd_fetch_char(true);
+
+ return c;
+}
+
+static void config_kbc(void)
+{
+ 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) * 4 + KBC_ROW_CFG0_0;
+ u32 c_offs = (i / 8) * 4 + KBC_COL_CFG0_0;
+
+ row_cfg = kbc_readl(r_offs);
+ col_cfg = kbc_readl(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;
+
+ kbc_writel(row_cfg, r_offs);
+ kbc_writel(col_cfg, c_offs);
+ }
+}
+
+static int tegra_kbc_open(void)
+{
+ u32 val = 0;
+
+ config_kbc();
+
+ kbc_writel(kbc->rpt_cnt, KBC_RPT_DLY_0);
+
+ val = kbc->debounce_cnt << 4;
+ val |= 1<<14; /* fifo interrupt threshold = 1 entry */
+ val |= 1<<3; /* interrupt on FIFO threshold reached */
+ val |= 1; /* enable */
+ kbc_writel(val, KBC_CONTROL_0);
+
+ /*
+ * atomically clear out any remaining entries in the key FIFO
+ * and enable keyboard interrupts.
+ */
+ while (1) {
+ val = kbc_readl(KBC_INT_0);
+ val >>= 4;
+ if (val) {
+ val = kbc_readl(KBC_KP_ENT0_0);
+ val = kbc_readl(KBC_KP_ENT1_0);
+ } else {
+ break;
+ }
+ }
+ kbc_writel(0x7, KBC_INT_0);
+
+ return 0;
+}
+
+int tegra_kbc_init(void)
+{
+ int error;
+ struct stdio_dev kbddev;
+ char *stdinname;
+
+ config_kbc_pinmux();
+ config_kbc_clock();
+
+ kbc = malloc(sizeof(*kbc));
+ if (!kbc)
+ return -1;
+
+ kbc->debounce_cnt = 2;
+ kbc->rpt_cnt = 5 * 32;
+ kbc->debounce_cnt = min(kbc->debounce_cnt, 0x3fful);
+ kbc->repoll_time = 5 + (16 + kbc->debounce_cnt) * 0x10 + kbc->rpt_cnt;
+ kbc->repoll_time = (kbc->repoll_time + 31) / 32;
+
+ kbc->plain_keycode = plain_kbd_keycode;
+ kbc->fn_keycode = fn_kbd_keycode;
+ kbc->shift_keycode = shift_kbd_keycode;
+
+ 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;
+}
+
« board/tegra2/seaboard/seaboard.c ('K') | « drivers/input/Makefile ('k') | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698