Patchwork [U-Boot,v5,1/3] PXA: PXA27x Matrix keypad driver

login
register
mail settings
Submitter Vasily Khoruzhick
Date Jan. 24, 2012, 7:41 p.m.
Message ID <1327434092-21410-1-git-send-email-anarsoul@gmail.com>
Download mbox | patch
Permalink /patch/137623/
State Superseded
Headers show

Comments

Vasily Khoruzhick - Jan. 24, 2012, 7:41 p.m.
From: Marek Vasut <marek.vasut@gmail.com>

Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
---
v2: use struct-based access to regs, minor cleanup
v3: fix multiple keypresses handling, minor cleanup
v4: another minor cleanup
v5: fix indentation issues in scan_keys(), remove udelay,
    increase PXA_KEYPAD_TIMEOUT (due to removed udelay)

 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                  |  292 +++++++++++++++++++++++++++
 4 files changed, 378 insertions(+), 52 deletions(-)
 create mode 100644 arch/arm/include/asm/arch-pxa/regs-keypad.h
 create mode 100644 drivers/input/pxa27x-mkp.c
Marek Vasut - Jan. 25, 2012, 12:25 a.m.
> From: Marek Vasut <marek.vasut@gmail.com>
> 
> Signed-off-by: Marek Vasut <marek.vasut@gmail.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul@gmail.com>
> ---
> v2: use struct-based access to regs, minor cleanup
> v3: fix multiple keypresses handling, minor cleanup
> v4: another minor cleanup
> v5: fix indentation issues in scan_keys(), remove udelay,
>     increase PXA_KEYPAD_TIMEOUT (due to removed udelay)
> 
>  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                  |  292
> +++++++++++++++++++++++++++ 4 files changed, 378 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..1909417
> --- /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..0c9a905
> --- /dev/null
> +++ b/drivers/input/pxa27x-mkp.c
> @@ -0,0 +1,292 @@
> +/*
> + * PXA27x matrix keypad controller driver
> + *
> + * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
> + * Copyright (C) 2012 Vasily Khoruzhick <anarsoul@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 = (struct pxa_keypad_regs
> *)KEYPAD_BASE; +
> +/* autorepeat stuff */
> +static int last_key_row = 0xff, last_key_col = 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
> +
> +/* Number of cycles to wait */
> +#define PXA_KEYPAD_TIMEOUT	100000
> +
> +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 int kbd_lookup(int row, int col, int mod)
> +{
> +	int i = 0;
> +	char key = 0xff;
> +
> +	while (!(keymap[i].col == 0xff && keymap[i].row == 0xff)) {
> +		if (keymap[i].row != row || keymap[i].col != col) {
> +			i++;
> +			continue;
> +		}
> +		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) {
> +			i++;
> +			continue;
> +		}
> +
> +		if (row != last_key_row || col != last_key_col) {
> +			queue[queue_len++] = key;
> +			last_key_row = row;
> +			last_key_col = col;
> +			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++;
> +	}
> +	return key;
> +}
> +
> +static int scan_keys(int scan_modif, uint32_t kpasmkp[4])
> +{
> +	uint32_t reg = 0;
> +	int col, row;
> +	static int mod = MOD_NONE;
> +	int key;
> +	for (col = 0; col < 8; col += 1) {
> +		reg = kpasmkp[col >> 1];
> +		reg >>= 16 * (col % 2);
> +		for (row = 0; row < 8; row++) {
> +			if (!(reg & (1 << row)))
> +				continue;
> +
> +			if (scan_modif) {
> +				mod = kbd_get_mdf(row, col);
> +				if (mod != MOD_NONE)
> +					return mod;
> +			} else {
> +				key = kbd_lookup(row, col, mod);
> +				if (key != 0xff)
> +					return key;
> +			}
> +		}
> +	}
> +
> +	if (scan_modif)
> +		return MOD_NONE;
> +	else
> +		return 0xff;
> +}
> +
> +static void kbd_read(void)
> +{
> +	uint32_t reg;
> +	int col, row, i, have_new_key = 0;
> +	int numkeys;
> +	int mod = MOD_NONE;
> +	unsigned int timeout = PXA_KEYPAD_TIMEOUT;
> +	static uint32_t kpasmkp_old[4];
> +	uint32_t kpasmkp[4], kpasmkp_diff[4];
> +
> +	/* start one automatic scan */
> +	writel(readl(&regs->kpc) | KPC_AS, &regs->kpc);
> +
> +	/* wait for scan to finish */
> +	while (timeout--) {
> +		if (!(readl(&regs->kpc) & KPC_AS))
> +			break;
> +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
> +		WATCHDOG_RESET();
> +#endif
> +	}
> +
> +	if (!timeout)
> +		return;
> +
> +	numkeys = (readl(&regs->kpas) >> 26) & 0x1f;
> +	switch (numkeys) {
> +	case 0:
> +		/* no key pressed, clear autorepeat counter */
> +		key_counter = 0;
> +		last_key_row = last_key_col = 0xff;
> +		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++)
> +			kpasmkp[i] = 0;
> +		break;
> +	case 1:
> +		reg = readl(&regs->kpas) & 0xff;
> +		col = reg & 0x0f;
> +		row = reg >> 4;
> +		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++)
> +			kpasmkp[i] = 0;
> +		kpasmkp[col >> 1] |= (1 << (row + 16 * (col % 2)));
> +
> +		break;
> +	default:
> +		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++) {
> +			timeout = PXA_KEYPAD_TIMEOUT;
> +			while (timeout--) {
> +				kpasmkp[i] = readl(&regs->kpasmkp[i].kpasmkpx);
> +				if (!(kpasmkp[i] & KPASMKPx_SO))
> +					break;
> +#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)

Remove this above

> +				WATCHDOG_RESET();
> +#endif
> +			}
> +			if (!timeout)
> +				kpasmkp[i] = 0;
> +		}
> +		break;
> +	}
> +
> +	/* Find new keypress */
> +	for (i = 0; i < ARRAY_SIZE(kpasmkp); i++) {
> +		kpasmkp_diff[i] = (kpasmkp_old[i] ^ kpasmkp[i]) &
> +			kpasmkp[i];
> +		if (kpasmkp_diff[i])
> +			have_new_key = 1;
> +		kpasmkp_old[i] = kpasmkp[i];
> +	}
> +
> +	if (!numkeys)
> +		return;
> +
> +	/* Scan for modifiers */
> +	mod = scan_keys(1, kpasmkp);
> +	if (!have_new_key) {
> +		/* Check if old key is still pressed */
> +		if (kpasmkp[last_key_col >> 1] &
> +		   (1 << (last_key_row + 16 * (last_key_col % 2))))
> +			kbd_lookup(last_key_row, last_key_col, mod);
> +
> +	} else {
> +		key_counter = 0;
> +		last_key_row = last_key_col = 0xff;
> +		scan_keys(0, kpasmkp_diff);
> +	}
> +}
> +
> +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;
> +
> +	writel((CONFIG_PXA27X_MKP_MKP_ROWS << 26) |
> +		(CONFIG_PXA27X_MKP_MKP_COLS << 23) |
> +		(0xff << 13) | KPC_ME, &regs->kpc);
> +	writel(CONFIG_PXA27X_MKP_DEBOUNCE, &regs->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;
> +}

Patch

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..1909417
--- /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..0c9a905
--- /dev/null
+++ b/drivers/input/pxa27x-mkp.c
@@ -0,0 +1,292 @@ 
+/*
+ * PXA27x matrix keypad controller driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut@gmail.com>
+ * Copyright (C) 2012 Vasily Khoruzhick <anarsoul@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 = (struct pxa_keypad_regs *)KEYPAD_BASE;
+
+/* autorepeat stuff */
+static int last_key_row = 0xff, last_key_col = 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
+
+/* Number of cycles to wait */
+#define PXA_KEYPAD_TIMEOUT	100000
+
+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 int kbd_lookup(int row, int col, int mod)
+{
+	int i = 0;
+	char key = 0xff;
+
+	while (!(keymap[i].col == 0xff && keymap[i].row == 0xff)) {
+		if (keymap[i].row != row || keymap[i].col != col) {
+			i++;
+			continue;
+		}
+		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) {
+			i++;
+			continue;
+		}
+
+		if (row != last_key_row || col != last_key_col) {
+			queue[queue_len++] = key;
+			last_key_row = row;
+			last_key_col = col;
+			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++;
+	}
+	return key;
+}
+
+static int scan_keys(int scan_modif, uint32_t kpasmkp[4])
+{
+	uint32_t reg = 0;
+	int col, row;
+	static int mod = MOD_NONE;
+	int key;
+	for (col = 0; col < 8; col += 1) {
+		reg = kpasmkp[col >> 1];
+		reg >>= 16 * (col % 2);
+		for (row = 0; row < 8; row++) {
+			if (!(reg & (1 << row)))
+				continue;
+
+			if (scan_modif) {
+				mod = kbd_get_mdf(row, col);
+				if (mod != MOD_NONE)
+					return mod;
+			} else {
+				key = kbd_lookup(row, col, mod);
+				if (key != 0xff)
+					return key;
+			}
+		}
+	}
+
+	if (scan_modif)
+		return MOD_NONE;
+	else
+		return 0xff;
+}
+
+static void kbd_read(void)
+{
+	uint32_t reg;
+	int col, row, i, have_new_key = 0;
+	int numkeys;
+	int mod = MOD_NONE;
+	unsigned int timeout = PXA_KEYPAD_TIMEOUT;
+	static uint32_t kpasmkp_old[4];
+	uint32_t kpasmkp[4], kpasmkp_diff[4];
+
+	/* start one automatic scan */
+	writel(readl(&regs->kpc) | KPC_AS, &regs->kpc);
+
+	/* wait for scan to finish */
+	while (timeout--) {
+		if (!(readl(&regs->kpc) & KPC_AS))
+			break;
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+		WATCHDOG_RESET();
+#endif
+	}
+
+	if (!timeout)
+		return;
+
+	numkeys = (readl(&regs->kpas) >> 26) & 0x1f;
+	switch (numkeys) {
+	case 0:
+		/* no key pressed, clear autorepeat counter */
+		key_counter = 0;
+		last_key_row = last_key_col = 0xff;
+		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++)
+			kpasmkp[i] = 0;
+		break;
+	case 1:
+		reg = readl(&regs->kpas) & 0xff;
+		col = reg & 0x0f;
+		row = reg >> 4;
+		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++)
+			kpasmkp[i] = 0;
+		kpasmkp[col >> 1] |= (1 << (row + 16 * (col % 2)));
+
+		break;
+	default:
+		for (i = 0; i < ARRAY_SIZE(kpasmkp); i++) {
+			timeout = PXA_KEYPAD_TIMEOUT;
+			while (timeout--) {
+				kpasmkp[i] = readl(&regs->kpasmkp[i].kpasmkpx);
+				if (!(kpasmkp[i] & KPASMKPx_SO))
+					break;
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+				WATCHDOG_RESET();
+#endif
+			}
+			if (!timeout)
+				kpasmkp[i] = 0;
+		}
+		break;
+	}
+
+	/* Find new keypress */
+	for (i = 0; i < ARRAY_SIZE(kpasmkp); i++) {
+		kpasmkp_diff[i] = (kpasmkp_old[i] ^ kpasmkp[i]) &
+			kpasmkp[i];
+		if (kpasmkp_diff[i])
+			have_new_key = 1;
+		kpasmkp_old[i] = kpasmkp[i];
+	}
+
+	if (!numkeys)
+		return;
+
+	/* Scan for modifiers */
+	mod = scan_keys(1, kpasmkp);
+	if (!have_new_key) {
+		/* Check if old key is still pressed */
+		if (kpasmkp[last_key_col >> 1] &
+		   (1 << (last_key_row + 16 * (last_key_col % 2))))
+			kbd_lookup(last_key_row, last_key_col, mod);
+
+	} else {
+		key_counter = 0;
+		last_key_row = last_key_col = 0xff;
+		scan_keys(0, kpasmkp_diff);
+	}
+}
+
+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;
+
+	writel((CONFIG_PXA27X_MKP_MKP_ROWS << 26) |
+		(CONFIG_PXA27X_MKP_MKP_COLS << 23) |
+		(0xff << 13) | KPC_ME, &regs->kpc);
+	writel(CONFIG_PXA27X_MKP_DEBOUNCE, &regs->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;
+}