[U-Boot] [PATCH 1/4] PXA: PXA27x Matrix keypad driver

Marek Vasut marek.vasut at gmail.com
Mon Jan 9 14:12:13 CET 2012


> From: Marek Vasut <marek.vasut at gmail.com>
> 
> Signed-off-by: Marek Vasut <marek.vasut at gmail.com>
> Signed-off-by: Vasily Khoruzhick <anarsoul at 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 */

NAK, use struct based access.

> 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.vasut at 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

Make this configurable
> +
> +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;

Don't declare this here

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

Shift the rest of the stuff to the left by one tab.

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

; on new line

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

This really needs cleanup. I'm somewhat ashamed I hacked together such a code :)

M


More information about the U-Boot mailing list