Message ID | 1326319289-7065-1-git-send-email-anarsoul@gmail.com |
---|---|
State | Superseded |
Headers | show |
> From: Marek Vasut <marek.vasut@gmail.com> > > Signed-off-by: Marek Vasut <marek.vasut@gmail.com> > Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com> > [vasily: adapted Marek's old version for newer u-boot] > --- > v2: use struct-based access to regs, minor cleanup > > arch/arm/include/asm/arch-pxa/pxa-regs.h | 52 ------ > arch/arm/include/asm/arch-pxa/regs-keypad.h | 84 +++++++++ > drivers/input/Makefile | 2 + > drivers/input/pxa27x-mkp.c | 245 > +++++++++++++++++++++++++++ 4 files changed, 331 insertions(+), 52 > deletions(-) > create mode 100644 arch/arm/include/asm/arch-pxa/regs-keypad.h > 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..d562658 100644 > --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h > +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h > @@ -2567,58 +2567,6 @@ typedef void (*ExcpHndlr) (void) ; > #define OVL2C1_O2EN (1<<31) /* Enable bit for Overlay 2 */ > #define CCR_CEN (1<<31) /* Enable bit for Cursor */ > > -/* Keypad controller */ > - > -#define KPC 0x41500000 /* Keypad Interface Control register */ > -#define KPDK 0x41500008 /* Keypad Interface Direct Key register */ > -#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 > KPKDI 0x41500048 /* Keypad Interface Key Debounce Interval register */ - > -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ > -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ > -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ > -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ > -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ > -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ > -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ > -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ > -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ > -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ > -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ > -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ > -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ > -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ > -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Key Debounce select */ > -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ > -#define KPC_DEE0 (0x1 << 2) /* Rotary Encoder 0 Enable */ > -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ > -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ > - > -#define KPDK_DKP (0x1 << 31) > -#define KPDK_DK7 (0x1 << 7) > -#define KPDK_DK6 (0x1 << 6) > -#define KPDK_DK5 (0x1 << 5) > -#define KPDK_DK4 (0x1 << 4) > -#define KPDK_DK3 (0x1 << 3) > -#define KPDK_DK2 (0x1 << 2) > -#define KPDK_DK1 (0x1 << 1) > -#define KPDK_DK0 (0x1 << 0) > - > -#define KPREC_OF1 (0x1 << 31) > -#define kPREC_UF1 (0x1 << 30) > -#define KPREC_OF0 (0x1 << 15) > -#define KPREC_UF0 (0x1 << 14) > - > -#define KPMK_MKP (0x1 << 31) > -#define KPAS_SO (0x1 << 31) > -#define KPASMKPx_SO (0x1 << 31) > - > #define GPIO113_BIT (1 << 17)/* GPIO113 in GPSR, GPCR, bit 17 */ > #define PSLR 0x40F00034 > #define PSTR 0x40F00038 /* Power Manager Standby Configuration Reg */ > diff --git a/arch/arm/include/asm/arch-pxa/regs-keypad.h > b/arch/arm/include/asm/arch-pxa/regs-keypad.h new file mode 100644 > index 0000000..f13da56 > --- /dev/null > +++ b/arch/arm/include/asm/arch-pxa/regs-keypad.h > @@ -0,0 +1,84 @@ > +/* > + * Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com> > + * > + * 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 > + */ > + > +#ifndef __REGS_KEYPAD_H__ > +#define __REGS_KEYPAD_H__ > + > +#define KEYPAD_BASE 0x41500000 > + > +struct kpasmkp_regs { > + uint32_t kpasmkpx; > + uint32_t reserved; > +}; > + > +struct pxa_keypad_regs { > + uint32_t kpc; > + uint32_t reserved_1; > + uint32_t kpdk; > + uint32_t reserved_2; > + uint32_t kprec; > + uint32_t reserved_3; > + uint32_t kpmk; > + uint32_t reserved_4; > + uint32_t kpas; > + uint32_t reserved_5; > + struct kpasmkp_regs kpasmkp[4]; > + uint32_t kpkdi; > +}; > + > +#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ > +#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ > +#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ > +#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ > +#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ > +#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ > +#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ > +#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ > +#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ > +#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ > +#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ > +#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ > +#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ > +#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ > +#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Key Debounce select */ > +#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ > +#define KPC_DEE0 (0x1 << 2) /* Rotary Encoder 0 Enable */ > +#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ > +#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ > + > +#define KPDK_DKP (0x1 << 31) > +#define KPDK_DK7 (0x1 << 7) > +#define KPDK_DK6 (0x1 << 6) > +#define KPDK_DK5 (0x1 << 5) > +#define KPDK_DK4 (0x1 << 4) > +#define KPDK_DK3 (0x1 << 3) > +#define KPDK_DK2 (0x1 << 2) > +#define KPDK_DK1 (0x1 << 1) > +#define KPDK_DK0 (0x1 << 0) > + > +#define KPREC_OF1 (0x1 << 31) > +#define kPREC_UF1 (0x1 << 30) > +#define KPREC_OF0 (0x1 << 15) > +#define KPREC_UF0 (0x1 << 14) > + > +#define KPMK_MKP (0x1 << 31) > +#define KPAS_SO (0x1 << 31) > +#define KPASMKPx_SO (0x1 << 31) > + > +#endif > 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..f72db41 > --- /dev/null > +++ b/drivers/input/pxa27x-mkp.c > @@ -0,0 +1,245 @@ > +/* > + * PXA27x matrix keypad controller driver > + * > + * Copyright (C) 2010 Marek Vasut <marek.vasut@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/regs-keypad.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; > +static struct pxa_keypad_regs *regs; > + > +/* autorepeat stuff */ > +static unsigned char last_key = 0xff; > +static char key_counter; > + > +/* number of key scans before autorepeat kicks in */ > +#ifndef CONFIG_PXA27X_KEY_REPEAT_FIRST > +#define CONFIG_PXA27X_KEY_REPEAT_FIRST 12 > +#endif > +#ifndef CONFIG_PXA27X_KEY_REPEAT_NEXT > +#define CONFIG_PXA27X_KEY_REPEAT_NEXT 2 > +#endif > + > +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; > + static char key = 0xff; > + > + while (!(keymap[i].col == 0xff && keymap[i].row == 0xff)) { > + if (keymap[i].row == row && keymap[i].col == col) { if (!) { i++; continue }; ... rest ... > + 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) > + continue; > + > + if (key != last_key) { > + queue[queue_len++] = key; > + last_key = key; > + key_counter = 0; > + } else /* same key as before */ > + if (key_counter < > + CONFIG_PXA27X_KEY_REPEAT_FIRST) { > + /* ignore key press */ > + key_counter++; > + } else { > + /* ok, autorepeat */ > + queue[queue_len++] = key; > + key_counter = > + CONFIG_PXA27X_KEY_REPEAT_FIRST > + - CONFIG_PXA27X_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(®s->kpasmkp[col >> 1].kpasmkpx)) > + & 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; > + > + /* start one automatic scan */ > + writel(readl(®s->kpc) | KPC_AS, ®s->kpc); > + > + /* wait for scan to finish */ > + while (readl(®s->kpc) & KPC_AS) > + ; Add timeout here. > + > + numkeys = (readl(®s->kpas) >> 26) & 0x1f; > + switch (numkeys) { > + case 0: > + /* no key pressed, clear autorepeat counter */ > + last_key = 0xff; > + key_counter = 0; > + break; > + case 1: > + reg = readl(®s->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; > + > + regs = (struct pxa_keypad_regs *)KEYPAD_BASE; > + queue_len = 0; > + > + writel((CONFIG_PXA27X_MKP_MKP_ROWS << 26) | > + (CONFIG_PXA27X_MKP_MKP_COLS << 23) | > + (0xff << 13) | KPC_ME, ®s->kpc); > + writel(CONFIG_PXA27X_MKP_DEBOUNCE, ®s->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; > +}
diff --git a/arch/arm/include/asm/arch-pxa/pxa-regs.h b/arch/arm/include/asm/arch-pxa/pxa-regs.h index b81b42c..d562658 100644 --- a/arch/arm/include/asm/arch-pxa/pxa-regs.h +++ b/arch/arm/include/asm/arch-pxa/pxa-regs.h @@ -2567,58 +2567,6 @@ typedef void (*ExcpHndlr) (void) ; #define OVL2C1_O2EN (1<<31) /* Enable bit for Overlay 2 */ #define CCR_CEN (1<<31) /* Enable bit for Cursor */ -/* Keypad controller */ - -#define KPC 0x41500000 /* Keypad Interface Control register */ -#define KPDK 0x41500008 /* Keypad Interface Direct Key register */ -#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 KPKDI 0x41500048 /* Keypad Interface Key Debounce Interval register */ - -#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ -#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ -#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ -#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ -#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ -#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ -#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ -#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ -#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ -#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ -#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ -#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ -#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ -#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ -#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Key Debounce select */ -#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ -#define KPC_DEE0 (0x1 << 2) /* Rotary Encoder 0 Enable */ -#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ -#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ - -#define KPDK_DKP (0x1 << 31) -#define KPDK_DK7 (0x1 << 7) -#define KPDK_DK6 (0x1 << 6) -#define KPDK_DK5 (0x1 << 5) -#define KPDK_DK4 (0x1 << 4) -#define KPDK_DK3 (0x1 << 3) -#define KPDK_DK2 (0x1 << 2) -#define KPDK_DK1 (0x1 << 1) -#define KPDK_DK0 (0x1 << 0) - -#define KPREC_OF1 (0x1 << 31) -#define kPREC_UF1 (0x1 << 30) -#define KPREC_OF0 (0x1 << 15) -#define KPREC_UF0 (0x1 << 14) - -#define KPMK_MKP (0x1 << 31) -#define KPAS_SO (0x1 << 31) -#define KPASMKPx_SO (0x1 << 31) - #define GPIO113_BIT (1 << 17)/* GPIO113 in GPSR, GPCR, bit 17 */ #define PSLR 0x40F00034 #define PSTR 0x40F00038 /* Power Manager Standby Configuration Reg */ diff --git a/arch/arm/include/asm/arch-pxa/regs-keypad.h b/arch/arm/include/asm/arch-pxa/regs-keypad.h new file mode 100644 index 0000000..f13da56 --- /dev/null +++ b/arch/arm/include/asm/arch-pxa/regs-keypad.h @@ -0,0 +1,84 @@ +/* + * Copyright (C) 2012 Vasily Khoruzhick <anarsoul@gmail.com> + * + * 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 + */ + +#ifndef __REGS_KEYPAD_H__ +#define __REGS_KEYPAD_H__ + +#define KEYPAD_BASE 0x41500000 + +struct kpasmkp_regs { + uint32_t kpasmkpx; + uint32_t reserved; +}; + +struct pxa_keypad_regs { + uint32_t kpc; + uint32_t reserved_1; + uint32_t kpdk; + uint32_t reserved_2; + uint32_t kprec; + uint32_t reserved_3; + uint32_t kpmk; + uint32_t reserved_4; + uint32_t kpas; + uint32_t reserved_5; + struct kpasmkp_regs kpasmkp[4]; + uint32_t kpkdi; +}; + +#define KPC_AS (0x1 << 30) /* Automatic Scan bit */ +#define KPC_ASACT (0x1 << 29) /* Automatic Scan on Activity */ +#define KPC_MI (0x1 << 22) /* Matrix interrupt bit */ +#define KPC_IMKP (0x1 << 21) /* Ignore Multiple Key Press */ +#define KPC_MS7 (0x1 << 20) /* Matrix scan line 7 */ +#define KPC_MS6 (0x1 << 19) /* Matrix scan line 6 */ +#define KPC_MS5 (0x1 << 18) /* Matrix scan line 5 */ +#define KPC_MS4 (0x1 << 17) /* Matrix scan line 4 */ +#define KPC_MS3 (0x1 << 16) /* Matrix scan line 3 */ +#define KPC_MS2 (0x1 << 15) /* Matrix scan line 2 */ +#define KPC_MS1 (0x1 << 14) /* Matrix scan line 1 */ +#define KPC_MS0 (0x1 << 13) /* Matrix scan line 0 */ +#define KPC_ME (0x1 << 12) /* Matrix Keypad Enable */ +#define KPC_MIE (0x1 << 11) /* Matrix Interrupt Enable */ +#define KPC_DK_DEB_SEL (0x1 << 9) /* Direct Key Debounce select */ +#define KPC_DI (0x1 << 5) /* Direct key interrupt bit */ +#define KPC_DEE0 (0x1 << 2) /* Rotary Encoder 0 Enable */ +#define KPC_DE (0x1 << 1) /* Direct Keypad Enable */ +#define KPC_DIE (0x1 << 0) /* Direct Keypad interrupt Enable */ + +#define KPDK_DKP (0x1 << 31) +#define KPDK_DK7 (0x1 << 7) +#define KPDK_DK6 (0x1 << 6) +#define KPDK_DK5 (0x1 << 5) +#define KPDK_DK4 (0x1 << 4) +#define KPDK_DK3 (0x1 << 3) +#define KPDK_DK2 (0x1 << 2) +#define KPDK_DK1 (0x1 << 1) +#define KPDK_DK0 (0x1 << 0) + +#define KPREC_OF1 (0x1 << 31) +#define kPREC_UF1 (0x1 << 30) +#define KPREC_OF0 (0x1 << 15) +#define KPREC_UF0 (0x1 << 14) + +#define KPMK_MKP (0x1 << 31) +#define KPAS_SO (0x1 << 31) +#define KPASMKPx_SO (0x1 << 31) + +#endif 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..f72db41 --- /dev/null +++ b/drivers/input/pxa27x-mkp.c @@ -0,0 +1,245 @@ +/* + * PXA27x matrix keypad controller driver + * + * Copyright (C) 2010 Marek Vasut <marek.vasut@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/regs-keypad.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; +static struct pxa_keypad_regs *regs; + +/* autorepeat stuff */ +static unsigned char last_key = 0xff; +static char key_counter; + +/* number of key scans before autorepeat kicks in */ +#ifndef CONFIG_PXA27X_KEY_REPEAT_FIRST +#define CONFIG_PXA27X_KEY_REPEAT_FIRST 12 +#endif +#ifndef CONFIG_PXA27X_KEY_REPEAT_NEXT +#define CONFIG_PXA27X_KEY_REPEAT_NEXT 2 +#endif + +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; + static char key = 0xff; + + while (!(keymap[i].col == 0xff && keymap[i].row == 0xff)) { + if (keymap[i].row == row && keymap[i].col == col) { + 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) + continue; + + if (key != last_key) { + queue[queue_len++] = key; + last_key = key; + key_counter = 0; + } else /* same key as before */ + if (key_counter < + CONFIG_PXA27X_KEY_REPEAT_FIRST) { + /* ignore key press */ + key_counter++; + } else { + /* ok, autorepeat */ + queue[queue_len++] = key; + key_counter = + CONFIG_PXA27X_KEY_REPEAT_FIRST + - CONFIG_PXA27X_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(®s->kpasmkp[col >> 1].kpasmkpx)) + & 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; + + /* start one automatic scan */ + writel(readl(®s->kpc) | KPC_AS, ®s->kpc); + + /* wait for scan to finish */ + while (readl(®s->kpc) & KPC_AS) + ; + + numkeys = (readl(®s->kpas) >> 26) & 0x1f; + switch (numkeys) { + case 0: + /* no key pressed, clear autorepeat counter */ + last_key = 0xff; + key_counter = 0; + break; + case 1: + reg = readl(®s->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; + + regs = (struct pxa_keypad_regs *)KEYPAD_BASE; + queue_len = 0; + + writel((CONFIG_PXA27X_MKP_MKP_ROWS << 26) | + (CONFIG_PXA27X_MKP_MKP_COLS << 23) | + (0xff << 13) | KPC_ME, ®s->kpc); + writel(CONFIG_PXA27X_MKP_DEBOUNCE, ®s->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; +}