From: Marek Vasut <marek.va...@gmail.com> Signed-off-by: Marek Vasut <marek.va...@gmail.com> Signed-off-by: Vasily Khoruzhick <anars...@gmail.com> [vasily: adapted Marek's old version for newer u-boot] --- arch/arm/include/asm/arch-pxa/pxa-regs.h | 6 +- drivers/input/Makefile | 2 + drivers/input/pxa27x-mkp.c | 229 ++++++++++++++++++++++++++++++ 3 files changed, 233 insertions(+), 4 deletions(-) create mode 100644 drivers/input/pxa27x-mkp.c
diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index b81b42c..2886905 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -2574,10 +2574,8 @@ typedef void (*ExcpHndlr) (void) ; #define KPREC 0x41500010 /* Keypad Intefcace Rotary Encoder register */ #define KPMK 0x41500018 /* Keypad Intefcace Matrix Key register */ #define KPAS 0x41500020 /* Keypad Interface Automatic Scan register */ -#define KPASMKP0 0x41500028 /* Keypad Interface Automatic Scan Multiple Key Presser register 0 */ -#define KPASMKP1 0x41500030 /* Keypad Interface Automatic Scan Multiple Key Presser register 1 */ -#define KPASMKP2 0x41500038 /* Keypad Interface Automatic Scan Multiple Key Presser register 2 */ -#define KPASMKP3 0x41500040 /* Keypad Interface Automatic Scan Multiple Key Presser register 3 */ +#define KPASMKP(x) (0x41500028 + ((x) << 3)) /* Keypad Interface Automatic Scan + Multiple Key Presser registers */ #define KPKDI 0x41500048 /* Keypad Interface Key Debounce Interval register */ #define KPC_AS (0x1 << 30) /* Automatic Scan bit */ diff --git a/drivers/input/Makefile b/drivers/input/Makefile index 1f4dad3..792d29d 100644 --- a/drivers/input/Makefile +++ b/drivers/input/Makefile @@ -31,6 +31,8 @@ COBJS-y += keyboard.o pc_keyb.o COBJS-$(CONFIG_PS2MULT) += ps2mult.o ps2ser.o endif +COBJS-$(CONFIG_PXA27X_MKP) += pxa27x-mkp.o + COBJS := $(COBJS-y) SRCS := $(COBJS:.o=.c) OBJS := $(addprefix $(obj),$(COBJS)) diff --git a/drivers/input/pxa27x-mkp.c b/drivers/input/pxa27x-mkp.c new file mode 100644 index 0000000..cf59496 --- /dev/null +++ b/drivers/input/pxa27x-mkp.c @@ -0,0 +1,229 @@ +/* + * PXA27x matrix keypad controller driver + * + * Copyright (C) 2010 Marek Vasut <marek.va...@gmail.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 <stdio_dev.h> +#include <asm/arch/pxa-regs.h> +#include <asm/io.h> + +#define DEVNAME "pxa27x-mkp" + +struct { + char row; + char col; + char key; + char shift; + char alt; + char ctrl; +} keymap[] = { + CONFIG_PXA27X_MKP_KEYMAP, +}; + +static unsigned char queue[64] = {0}; +static int queue_len; + +/* autorepeat stuff */ +static unsigned char last_key = 0xff; +static char key_counter; + +/* number of key scans before autorepeat kicks in */ +#define KEY_REPEAT_FIRST 12 +#define KEY_REPEAT_NEXT 2 + +enum { + MOD_NONE, + MOD_SHIFT, + MOD_ALT, + MOD_CTRL, +}; + +static int kbd_get_mdf(int row, int col) +{ + char mod_shift[2] = CONFIG_PXA27X_MKP_MOD_SHIFT; + char mod_alt[2] = CONFIG_PXA27X_MKP_MOD_ALT; + char mod_ctrl[2] = CONFIG_PXA27X_MKP_MOD_CTRL; + + if (mod_shift[0] == row && mod_shift[1] == col) + return MOD_SHIFT; + if (mod_alt[0] == row && mod_alt[1] == col) + return MOD_ALT; + if (mod_ctrl[0] == row && mod_ctrl[1] == col) + return MOD_CTRL; + return MOD_NONE; +} + +static void kbd_lookup(int row, int col, int mod) +{ + int i = 0; + + while (!(keymap[i].col == 0xff && keymap[i].row == 0xff)) { + if (keymap[i].row == row && keymap[i].col == col) { + static char key = 0xff; + switch (mod) { + case MOD_NONE: + key = keymap[i].key; + break; + case MOD_SHIFT: + key = keymap[i].shift; + break; + case MOD_ALT: + key = keymap[i].alt; + break; + case MOD_CTRL: + key = keymap[i].ctrl; + break; + } + if (key != 0xff) { + if (key != last_key) { + queue[queue_len++] = key; + last_key = key; + key_counter = 0; + } else /* same key as before */ + if (key_counter < KEY_REPEAT_FIRST) { + /* ignore key press */ + key_counter++; + } else { + /* ok, autorepeat */ + queue[queue_len++] = key; + key_counter = KEY_REPEAT_FIRST + - KEY_REPEAT_NEXT; + } + } + } + i++; + } +} + +static void scan_keys(int modif) +{ + uint32_t reg; + int col, row; + int mod = MOD_NONE; + for (col = 0; col < 8; col += 2) { + while ((reg = readl(KPASMKP(col >> 1))) & KPASMKPx_SO); + for (row = 0; row < 8; row++) { + if (reg & (1 << row)) { + if (modif) { + mod = kbd_get_mdf(row, col); + if (mod != MOD_NONE) + return; + } else + kbd_lookup(row, col, mod); + } + if ((reg >> 16) & (1 << row)) { + if (modif) { + mod = kbd_get_mdf(row, col + 1); + if (mod != MOD_NONE) + return; + } else + kbd_lookup(row, col + 1, mod); + } + } + } +} + +static void kbd_read(void) +{ + uint32_t reg; + int col, row; + int modif = 0; + int numkeys; + int mod = MOD_NONE; + writel(readl(KPC) | KPC_AS, KPC); /* start one automatic scan */ + while (readl(KPC) & KPC_AS); /* wait for scan to finish */ + + numkeys = (readl(KPAS) >> 26) & 0x1f; + switch (numkeys) { + case 0: + /* no key pressed, clear autorepeat counter */ + last_key = 0xff; + key_counter = 0; + break; + case 1: + reg = readl(KPAS) & 0xff; + col = reg & 0x0f; + row = reg >> 4; + if (kbd_get_mdf(row, col) != MOD_NONE) { + /* modifier only */ + last_key = 0xff; + /* no real key, clear autorepeat counter */ + key_counter = 0; + } else + kbd_lookup(row, col, mod); + break; + default: + /* multiple keys pressed, check KPASMKPx registers */ + /* First scan for modifiers */ + scan_keys(1); + /* Second for other keys */ + scan_keys(0); + break; + } +} + +static int kbd_getc(void) +{ + if (!queue_len) { + kbd_read(); + udelay(CONFIG_PXA27X_MKP_DELAY); + } + + if (queue_len) + return queue[--queue_len]; + else + return 0; +} + +static int kbd_testc(void) +{ + if (!queue_len) + kbd_read(); + return queue_len; +} + +int drv_keyboard_init(void) +{ + int error = 0; + struct stdio_dev kbddev; + if (!keymap) + return -1; + + queue_len = 0; + + writel((CONFIG_PXA27X_MKP_MKP_ROWS << 26) | + (CONFIG_PXA27X_MKP_MKP_COLS << 23) | + (0xff << 13) | KPC_ME, KPC); + writel(CONFIG_PXA27X_MKP_DEBOUNCE, KPKDI); + + 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 ; + + error = stdio_register(&kbddev); + return error; +} -- 1.7.8.3 _______________________________________________ U-Boot mailing list U-Boot@lists.denx.de http://lists.denx.de/mailman/listinfo/u-boot