diff --git a/drivers/misc/Makefile b/drivers/misc/Makefile
index f6df60f..76c009a 100644
--- a/drivers/misc/Makefile
+++ b/drivers/misc/Makefile
@@ -30,6 +30,7 @@ COBJS-$(CONFIG_DS4510)  += ds4510.o
 COBJS-$(CONFIG_FSL_LAW) += fsl_law.o
 COBJS-$(CONFIG_NS87308) += ns87308.o
 COBJS-$(CONFIG_STATUS_LED) += status_led.o
+COBJS-$(CONFIG_STMPE2401) += stmpe2401.o
 COBJS-$(CONFIG_TWL4030_LED) += twl4030_led.o
 
 COBJS	:= $(COBJS-y)
diff --git a/drivers/misc/stmpe2401.c b/drivers/misc/stmpe2401.c
new file mode 100644
index 0000000..f347d07
--- /dev/null
+++ b/drivers/misc/stmpe2401.c
@@ -0,0 +1,191 @@
+/*
+ * board/st/nhk8815/egpio.c: extended gpio as found on nhk8815 board
+ *
+ * Copyright 2009 Alessandro Rubini <rubini@unipv.it>
+ *
+ * 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 <i2c.h>
+#include <stmpe2401.h>
+
+/*
+ * First, an interface to set and read registers, used in this file as well
+ */
+int pe_getreg(int addr, int reg)
+{
+	unsigned char val8 = reg;
+	int ret;
+
+	ret = i2c_read(addr, reg, 1 /* len */, &val8, 1);
+	if (ret < 0)
+		return ret;
+	return val8;
+}
+
+int pe_setreg(int addr, int reg, int val)
+{
+	unsigned char val8 = val;
+
+	return i2c_write(addr, reg, 1, &val8, 1);
+}
+
+/*
+ * Generic higher-level GPIO interface
+ */
+int pe_gpio_af(int addr, int pin, int af)
+{
+	int regval;
+
+	regval = pe_getreg(addr, PE_GPIO_AFR(pin));
+	if (regval < 0)
+		return regval;
+	regval &= ~PE_GPIO_AF_MASK(pin);
+	regval |= af << PE_GPIO_AF_SHIFT(pin);
+	return pe_setreg(addr, PE_GPIO_AFR(pin), regval);
+}
+
+int pe_gpio_dir(int addr, int pin, int dir)
+{
+	int regval;
+
+	/* 0 == input, 1 == output */
+	regval = pe_getreg(addr, PE_GPIO_GPDR(pin));
+	if (regval < 0)
+		return regval;
+	regval &= ~PE_GPIO_MASK(pin);
+	if (dir)
+		regval |= PE_GPIO_MASK(pin);
+	return pe_setreg(addr, PE_GPIO_GPDR(pin), regval);
+}
+
+int pe_gpio_pud(int addr, int pin, int pu, int pd)
+{
+	int regval, mask = PE_GPIO_MASK(pin);
+
+	/* change pullup */
+	regval = pe_getreg(addr, PE_GPIO_GPPUR(pin));
+	if (regval < 0)
+		return regval;
+	if (pu)
+		regval |= mask;
+	else
+		regval &= ~mask;
+	regval = pe_setreg(addr, PE_GPIO_GPPUR(pin), regval);
+	if (regval < 0)
+		return regval;
+
+	/* change pulldown */
+	regval = pe_getreg(addr, PE_GPIO_GPPDR(pin));
+	if (regval < 0)
+		return regval;
+	if (pu)
+		regval |= mask;
+	else
+		regval &= ~mask;
+	regval = pe_setreg(addr, PE_GPIO_GPPDR(pin), regval);
+	if (regval < 0)
+		return regval;
+
+	return 0;
+}
+
+int pe_gpio_set(int addr, int pin, int val)
+{
+	int reg;
+
+	if (val)
+		reg = PE_GPIO_GPSR(pin);
+	else
+		reg = PE_GPIO_GPCR(pin);
+
+	return pe_setreg(addr, reg, PE_GPIO_MASK(pin));
+}
+
+int pe_gpio_get(int addr, int pin)
+{
+	int regval;
+
+	regval = pe_getreg(addr, PE_GPIO_GPMR(pin));
+	if (regval < 0)
+		return regval;
+	return (regval & PE_GPIO_MASK(pin)) ? 1 : 0;
+}
+
+/*
+ * Generic higher-level keypad interface: we have 12 rows out, 8 columns in
+ */
+int pe_kpc_init(int addr, int rowmask, int colmask, int debounce_ms)
+{
+	int i;
+	/* note that gpio15 is missing in the rows, so use tables */
+	static unsigned char row_to_gpio[12] = {
+		8, 9, 10, 11,   12, 13, 14, 16,   17, 18, 19, 20};
+	static unsigned char col_to_gpio[8] = {
+		0, 1, 2, 3,   4, 5, 6, 7};
+
+	/* First, configure pins for alternate functions (and pullup) */
+	for (i = 0; i < ARRAY_SIZE(row_to_gpio); i++) {
+		if (rowmask & (1 << i)) {
+			pe_gpio_dir(addr, row_to_gpio[i], 1 /* out */);
+			pe_gpio_af(addr, row_to_gpio[i], PE_GPIO_AF_AF1);
+			pe_gpio_pud(addr, row_to_gpio[i], 0, 0);
+		}
+	}
+	for (i = 0; i < ARRAY_SIZE(col_to_gpio); i++) {
+		if (colmask & (1 << i)) {
+			pe_gpio_dir(addr, col_to_gpio[i], 0 /* in */);
+			pe_gpio_af(addr, col_to_gpio[i], PE_GPIO_AF_AF1);
+			pe_gpio_pud(addr, col_to_gpio[i], 1, 0);
+		}
+	}
+
+	/* Set configuration for kpc: no support for dedicated keys */
+	pe_setreg(addr, PE_KPC_COL, colmask);
+	pe_setreg(addr, PE_KPC_ROW_MSB, 0xc0 | (rowmask >> 8));
+	pe_setreg(addr, PE_KPC_ROW_LSB, rowmask & 0xff);
+	pe_setreg(addr, PE_KPC_CTRL_MSB, 0x30 /* scan count is 3 */);
+	pe_setreg(addr, PE_KPC_CTRL_LSB, debounce_ms << 1);
+
+	/* Configure interrupt controller */
+	pe_setreg(addr, PE_ICR_LSB, 0x1); /* level, active low */
+	pe_setreg(addr, PE_IER_LSB, 0x2); /* bit1: keypad */
+
+	/* Start scanning */
+	pe_setreg(addr, PE_KPC_CTRL_LSB, (debounce_ms << 1) | 1);
+	return 0;
+}
+
+int pe_kpc_getkey(int addr, int *row, int *col)
+{
+	int key0, key1;
+
+	/* ack irq: bit 1 is keypad */
+	pe_setreg(addr, PE_ISR_LSB, 0x2);
+	/* get data -- one key only at a time: ignore key1*/
+	key0 = pe_getreg(addr, PE_KPC_DATA0);
+	key1 = pe_getreg(addr, PE_KPC_DATA1);
+	if (key0 & 0x80) /* release: return error */
+		return -1;
+	if ((key0 & 0x78) == 0x78) /* no key reported */
+		return -1;
+	*row = ((key0 & 0x78) >> 3);
+	*col = key0 & 0x07;
+	return 0;
+}
diff --git a/include/stmpe2401.h b/include/stmpe2401.h
new file mode 100644
index 0000000..3f513df
--- /dev/null
+++ b/include/stmpe2401.h
@@ -0,0 +1,66 @@
+/*
+ * Defines and rototypes for port extender STMPE2401. Use "PE_" as short prefix.
+ */
+
+#ifndef __STMPE2401_H
+#define __STMPE2401_H
+
+/*
+ * registers for the EGPIO blocks: we have groups of three registers,
+ * starting from MSB, so use negative offsets from LSB.
+ */
+#define PE_GPIO_OFFSET(gpio)  (- (gpio) / 8)
+#define PE_GPIO_MASK(gpio) (1 << ((gpio) & 7))
+
+#define PE_GPIO_GPMR(gpio)	(0xa4 + PE_GPIO_OFFSET(gpio)) /* monitor */
+#define PE_GPIO_GPCR(gpio)	(0x88 + PE_GPIO_OFFSET(gpio)) /* clear */
+#define PE_GPIO_GPSR(gpio)	(0x85 + PE_GPIO_OFFSET(gpio)) /* set */
+#define PE_GPIO_GPDR(gpio)	(0x8b + PE_GPIO_OFFSET(gpio)) /* direction */
+#define PE_GPIO_GPPUR(gpio)	(0x97 + PE_GPIO_OFFSET(gpio)) /* pull-up */
+#define PE_GPIO_GPPDR(gpio)	(0x9a + PE_GPIO_OFFSET(gpio)) /* pull-down */
+
+/* for alternate function, we have two bits per gpio, so 6 registers */
+#define PE_GPIO_AF_OFFSET(gpio)	(- (gpio) / 4)
+#define PE_GPIO_AF_SHIFT(gpio)	(2 * ((gpio) & 3))
+#define PE_GPIO_AF_MASK(gpio)	(3 << PE_GPIO_AF_SHIFT(gpio))
+#define PE_GPIO_AFR(gpio)	(0xa0 + PE_GPIO_AF_OFFSET(gpio))
+
+enum egpio_af {
+	PE_GPIO_AF_GPIO = 0,
+	PE_GPIO_AF_AF1,
+	PE_GPIO_AF_AF2,
+	PE_GPIO_AF_AF3
+};
+
+/* keypad controller registers */
+#define PE_KPC_COL		0x60
+#define PE_KPC_ROW_MSB		0x61
+#define PE_KPC_ROW_LSB		0x62
+#define PE_KPC_CTRL_MSB		0x63
+#define PE_KPC_CTRL_LSB		0x64
+#define PE_KPC_DATA0		0x68
+#define PE_KPC_DATA1		0x69
+#define PE_KPC_DATA2		0x6a
+
+/* interrupt controller registers (not all of them: we only need the LSB) */
+#define PE_ICR_LSB		0x11 /* control reg */
+#define PE_IER_LSB		0x13 /* enable reg */
+#define PE_ISR_LSB		0x15 /* status reg */
+
+/*
+ * prototypes of public functions
+ */
+extern int pe_getreg(int addr, int reg);
+extern int pe_setreg(int addr, int reg, int val);
+
+extern int pe_gpio_af(int addr, int gpio, int af);
+extern int pe_gpio_dir(int addr, int gpio, int dir);
+extern int pe_gpio_pud(int addr, int gpio, int pu, int pd);
+extern int pe_gpio_set(int addr, int gpio, int val);
+extern int pe_gpio_get(int addr, int gpio);
+
+/* here, rowmask is bits 0..11 for outputs, colmask is bits 0..7, for inputs */
+extern int pe_kpc_init(int addr, int rowmask, int colmask, int debounce_ms);
+extern int pe_kpc_getkey(int addr, int *row, int *col);
+
+#endif /* __STMPE2401_H */
