[U-Boot] [PATCH v4 1/3] PXA: PXA27x Matrix keypad driver
Vasily Khoruzhick
anarsoul at gmail.com
Sat Jan 14 23:38:45 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,
fixed multiple keypresses handling]
---
v2: use struct-based access to regs, minor cleanup
v3: fix multiple keypresses handling, minor cleanup
v4: another 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 | 293 +++++++++++++++++++++++++++
4 files changed, 379 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 at 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..5368b57
--- /dev/null
+++ b/drivers/input/pxa27x-mkp.c
@@ -0,0 +1,293 @@
+/*
+ * PXA27x matrix keypad controller driver
+ *
+ * Copyright (C) 2010 Marek Vasut <marek.vasut at gmail.com>
+ * Copyright (C) 2012 Vasily Khoruzhick <anarsoul 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/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
+
+/* 100 milliseconds (in wait cycles below it's 100 x 10uS waits) */
+#define PXA_KEYPAD_TIMEOUT 10000
+
+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)) {
+ 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(®s->kpc) | KPC_AS, ®s->kpc);
+
+ /* wait for scan to finish */
+ while (timeout--) {
+ if (!(readl(®s->kpc) & KPC_AS))
+ break;
+ udelay(10);
+#if defined(CONFIG_HW_WATCHDOG) || defined(CONFIG_WATCHDOG)
+ WATCHDOG_RESET();
+#endif
+ }
+
+ if (!timeout)
+ return;
+
+ numkeys = (readl(®s->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(®s->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(®s->kpasmkp[i].kpasmkpx);
+ if (!(kpasmkp[i] & KPASMKPx_SO))
+ break;
+ udelay(10);
+#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, ®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;
+}
--
1.7.8.3
More information about the U-Boot
mailing list