| 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;
|
| +}
|
| +
|
|
|