
From: Alessandro Rubini rubini@unipv.it
Signed-off-by: Alessandro Rubini rubini@unipv.it Acked-by: Andrea Gallo andrea.gallo@stericsson.com --- drivers/misc/Makefile | 1 + drivers/misc/stmpe2401.c | 176 ++++++++++++++++++++++++++++++++++++++++++++++ include/stmpe2401.h | 66 +++++++++++++++++ 3 files changed, 243 insertions(+), 0 deletions(-) create mode 100644 drivers/misc/stmpe2401.c create mode 100644 include/stmpe2401.h
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..9bab1b4 --- /dev/null +++ b/drivers/misc/stmpe2401.c @@ -0,0 +1,176 @@ +/* + * 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..fe7691e --- /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 */