From patchwork Sat Dec 5 12:39:51 2009 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Alessandro Rubini X-Patchwork-Id: 71688 X-Patchwork-Delegate: albert.aribaud@free.fr Return-Path: X-Original-To: wd@gemini.denx.de Delivered-To: wd@gemini.denx.de Received: from diddl.denx.de (diddl.denx.de [10.0.0.6]) by gemini.denx.de (Postfix) with ESMTP id 4067EE34A46 for ; Sat, 5 Dec 2009 13:41:43 +0100 (CET) Received: from diddl.denx.de (localhost.localdomain [127.0.0.1]) by diddl.denx.de (Postfix) with ESMTP id 32750C97CB54 for ; Sat, 5 Dec 2009 13:41:43 +0100 (MET) Received: from pop.mnet-online.de by diddl.denx.de with POP3 (fetchmail-6.3.9) for (single-drop); Sat, 05 Dec 2009 13:41:43 +0100 (MET) Received: from murder (svr19.m-online.net [192.168.3.147]) by backend2 (Cyrus v2.2.12) with LMTPA; Sat, 05 Dec 2009 13:40:42 +0100 X-Sieve: CMU Sieve 2.2 Received: from mail.m-online.net (localhost [127.0.0.1]) by frontend3.pop.m-online.net (Cyrus v2.2.13) with LMTPA; Sat, 05 Dec 2009 13:40:42 +0100 Received: from scanner-1.m-online.net (unknown [192.168.8.165]) by mail.m-online.net (Postfix) with ESMTP id 91860200084; Sat, 5 Dec 2009 13:40:42 +0100 (CET) Received: from mxin-1.m-online.net ([192.168.1.21]) by scanner-1.m-online.net (scanner-1.m-online.net [192.168.8.165]) (amavisd-new, port 10026) with ESMTP id 17691-03-5; Sat, 5 Dec 2009 13:40:41 +0100 (CET) Received: from theia.denx.de (theia.denx.de [85.214.87.163]) by mxin-2.m-online.net (Postfix) with ESMTP id 9A21746E1C2; Sat, 5 Dec 2009 13:40:40 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 13C28280D6; Sat, 5 Dec 2009 13:40:03 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id ALqpoSIzWCIF; Sat, 5 Dec 2009 13:40:02 +0100 (CET) Received: from theia.denx.de (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id BE7A2280DF; Sat, 5 Dec 2009 13:39:59 +0100 (CET) Received: from localhost (localhost [127.0.0.1]) by theia.denx.de (Postfix) with ESMTP id 72D6E280C1 for ; Sat, 5 Dec 2009 13:39:57 +0100 (CET) Received: from theia.denx.de ([127.0.0.1]) by localhost (theia.denx.de [127.0.0.1]) (amavisd-new, port 10024) with ESMTP id reyL6hH45SMd for ; Sat, 5 Dec 2009 13:39:57 +0100 (CET) Received: from mail.gnudd.com (mail2.gnudd.com [213.203.150.91]) by theia.denx.de (Postfix) with ESMTP id 98975280D2 for ; Sat, 5 Dec 2009 13:39:55 +0100 (CET) Received: from mail.gnudd.com (localhost [127.0.0.1]) by mail.gnudd.com (8.14.3/8.14.3/Debian-5) with ESMTP id nB5CdpJs026792; Sat, 5 Dec 2009 13:39:51 +0100 Received: (from rubini@localhost) by mail.gnudd.com (8.14.3/8.14.3/Submit) id nB5Cdp7v026791; Sat, 5 Dec 2009 13:39:51 +0100 Date: Sat, 5 Dec 2009 13:39:51 +0100 From: Alessandro Rubini To: u-boot@lists.denx.de Message-ID: MIME-Version: 1.0 Content-Disposition: inline X-Face: #Q; A)@_4.#>0+_%y]7aBr:c"ndLp&#+2?]J; lkse\^)FP^Lr5@O0{)J; 'nny4%74.fM' n)M >ISCj.KmsL/HTxz!:Ju'pnj'Gz&. Organization: GnuDD, Device Drivers, Embedded Systems, Courses References: In-Reply-To: Cc: STEricsson_nomadik_linux@list.st.com, andrea.gallo@stericsson.com Subject: [U-Boot] [PATCH V3 4/9] drivers/misc: add stmpe2401 port extender and keypad controller X-BeenThere: u-boot@lists.denx.de X-Mailman-Version: 2.1.9 Precedence: list List-Id: U-Boot discussion List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Sender: u-boot-bounces@lists.denx.de Errors-To: u-boot-bounces@lists.denx.de X-Virus-Scanned: by amavisd-new at m-online.net From: Alessandro Rubini This driver is an i2c device acting as a port extender. Since the keypad can be configured to act on specific row and column lines, the specific setup is passed by the board file. This is used by the Nomadik nhk8815, through a later patch in this series. Signed-off-by: Alessandro Rubini Acked-by: Andrea Gallo --- drivers/misc/Makefile | 1 + drivers/misc/stmpe2401.c | 191 ++++++++++++++++++++++++++++++++++++++++++++++ include/stmpe2401.h | 66 ++++++++++++++++ 3 files changed, 258 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..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 + * + * 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 +#include +#include + +/* + * 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 */