@@ -231,6 +231,24 @@ config KEYBOARD_LM8323
To compile this driver as a module, choose M here: the
module will be called lm8323.
+config STMPE2401
+ tristate "STMPE2401 keypad support"
+ depends on I2C
+ help
+ Say Y or M here to have support for the ST Micro STMPE2401 GPIO
+ expander. The STMPE2401 provides up to twenty-four extra GPIO lines;
+ three of the lines can be reserved for programmable PWM channels,
+ another three lines can function as a dedicated rotator input, and
+ up to 20 lines can be used by the integrated matrix-keypad controller.
+ The matrix keypad controller allows up 12 rows and 8 columns of key
+ input. Each of the GPIO, PWM, and rotator subsystems have simultaneous
+ interrupt generation capabilities to alert the host system via GPIO.
+ The STMPE2401 also supports sleep and hibernate modes for low-power
+ devices. Currently, only the keypad functionality is implimented.
+
+ To compile this driver as a module, choose M here: the module
+ will be called stmpe2401.
+
config KEYBOARD_LOCOMO
tristate "LoCoMo Keyboard Support"
depends on SHARP_LOCOMO
@@ -18,6 +18,7 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_LKKBD) += lkkbd.o
obj-$(CONFIG_KEYBOARD_LM8323) += lm8323.o
+obj-$(CONFIG_STMPE2401) += stmpe2401.o
obj-$(CONFIG_KEYBOARD_LOCOMO) += locomokbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_MATRIX) += matrix_keypad.o
@@ -0,0 +1,1121 @@
+/*
+ * stmpe2401.c: driver for the STMPE2401 GPIO port expander
+ *
+ * Copyright (C) 2009 Christopher Friedt, <chrisfriedt@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., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Documentation for this device is available freely from ST Micro
+ *
+ * http://www.st.com/stonline/products/literature/ds/13018/stmpe2401.htm
+ * http://www.st.com/stonline/products/literature/ds/13018.pdf
+ * http://www.st.com/stonline/products/literature/an/12649.pdf
+ * http://www.st.com/stonline/products/literature/an/12648.pdf
+ * http://www.st.com/stonline/products/literature/an/12647.pdf
+ * http://www.st.com/stonline/products/literature/um/12788.pdf
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/irqnr.h>
+#include <linux/irq.h>
+#include <linux/gpio.h>
+#include <linux/platform_device.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+
+#include <linux/i2c/stmpe2401.h>
+
+/*--------------------------------------------------------------------------*/
+
+#define DRIVER_NAME "stmpe2401"
+
+/*--------------------------------------------------------------------------*/
+
+static inline u8 sum_bits( u32 x, u8 y ) {
+ u8 s;
+ for( s=0, --y; y >= 0; s += ((x >> y) & 1) ? 1 : 0, y-- );
+ return s;
+}
+#define sum_u8(x) sum_bits( (u32)x, 8 )
+#define sum_u16(x) sum_bits( (u32)x, 16 )
+#define sum_u32(x) sum_bits( (u32)x, 32 )
+
+/*--------------------------------------------------------------------------*/
+
+struct reg_conf {
+ u8 syscon; /* 0x02 */
+ u8 icr_msb; /* 0x10 */
+ u8 icr_lsb; /* 0x11 */
+ u8 ier_msb; /* 0x12 */
+ u8 ier_lsb; /* 0x13 */
+ u8 isr_msb; /* 0x14 */
+ u8 isr_lsb; /* 0x15 */
+ u8 iegpior_msb; /* 0x16 */
+ u8 iegpior_csb; /* 0x17 */
+ u8 iegpior_lsb; /* 0x18 */
+ u8 isgpior_msb; /* 0x19 */
+ u8 isgpior_csb; /* 0x1a */
+ u8 isgpior_lsb; /* 0x1b */
+ u8 pwmcs; /* 0x30 */
+ u8 pwmic0; /* 0x38 */
+ u8 pwmic1; /* 0x39 */
+ u8 pwmic2; /* 0x3a */
+ u8 kpc_col; /* 0x60 */
+ u8 kpc_row_msb; /* 0x61 */
+ u8 kpc_row_lsb; /* 0x62 */
+ u8 kpc_ctrl_msb; /* 0x63 */
+ u8 kpc_ctrl_lsb; /* 0x64 */
+ u8 kpc_data_byte0; /* 0x68 */
+ u8 kpc_data_byte1; /* 0x69 */
+ u8 kpc_data_byte2; /* 0x6a */
+ u8 chip_id; /* 0x80 */
+ u8 version_id; /* 0x81 */
+ u8 gpsr_msb; /* 0x83 */
+ u8 gpsr_csb; /* 0x84 */
+ u8 gpsr_lsb; /* 0x85 */
+ u8 gpcr_msb; /* 0x86 */
+ u8 gpcr_csb; /* 0x87 */
+ u8 gpcr_lsb; /* 0x88 */
+ u8 gpdr_msb; /* 0x89 */
+ u8 gpdr_csb; /* 0x8a */
+ u8 gpdr_lsb; /* 0x8b */
+ u8 gpedr_msb; /* 0x8c */
+ u8 gpedr_csb; /* 0x8d */
+ u8 gpedr_lsb; /* 0x8e */
+ u8 gprer_msb; /* 0x8f */
+ u8 gprer_csb; /* 0x90 */
+ u8 gprer_lsb; /* 0x91 */
+ u8 gpfer_msb; /* 0x92 */
+ u8 gpfer_csb; /* 0x93 */
+ u8 gpfer_lsb; /* 0x94 */
+ u8 gppur_msb; /* 0x95 */
+ u8 gppur_csb; /* 0x96 */
+ u8 gppur_lsb; /* 0x97 */
+ u8 gppdr_msb; /* 0x98 */
+ u8 gppdr_csb; /* 0x99 */
+ u8 gppdr_lsb; /* 0x9a */
+ u8 gpafr_u_msb; /* 0x9b */
+ u8 gpafr_u_csb; /* 0x9c */
+ u8 gpafr_u_lsb; /* 0x9d */
+ u8 gpafr_l_msb; /* 0x9e */
+ u8 gpafr_l_csb; /* 0x9f */
+ u8 gpafr_l_lsb; /* 0xa0 */
+ u8 gpmr_msb; /* 0xa2 */
+ u8 gpmr_csb; /* 0xa3 */
+ u8 gpmr_lsb; /* 0xa4 */
+};
+
+struct pwm;
+
+struct stmpe2401 {
+ struct i2c_client *cl;
+
+ u32 adapter_functionality;
+ u8 key_map_conf;
+ u8 kpd_read_once;
+
+ struct irq_chip *irq_chip;
+ struct gpio_chip *gio_chip;
+ struct input_dev *kpd_input_dev;
+ struct input_dev *rot_input_dev;
+ struct pwm *pwm[3];
+
+ // stmpe2401, kpd, pwm, rot, gio
+ struct work_struct work[5];
+
+ struct list_head list;
+ struct mutex lock;
+
+ // this is allocated for setup and then de-allocated
+ struct reg_conf *conf;
+};
+
+/*--------------------------------------------------------------------------*/
+
+static struct stmpe2401 instances = {
+ .list = LIST_HEAD_INIT( instances.list ),
+};
+
+/*--------------------------------------------------------------------------*/
+
+// [MSB] kpd->callback.custom, kpd->callback.setkeycode,
+// kpd->keymap.keycode, kpd->keymap.keycodemax [LSB]
+#define MAP_USE_CB 0x0f
+#define MAP_USE_KM_WITH_S 0x07
+#define MAP_USE_KM 0x03
+
+/*--------------------------------------------------------------------------*/
+
+/* usual argument order is client, command, length, (const) values */
+#define read_u8(x,y) i2c_smbus_read_byte_data(x,y)
+#define write_u8(x,y,z) i2c_smbus_write_byte_data(x,y,z)
+
+#define read_u16(x,y) i2c_smbus_read_word_data(x,y)
+#define write_u16(x,y,z) i2c_smbus_write_word_data(x,y,z)
+
+/*
+// TODO: need generic wrapper to handle various adapter functionality
+// until are more generic handler is available
+#define read_data(x,y,z) i2c_smbus_read_block_data(x,y,z)
+#define write_data(w,x,y,z) i2c_smbus_write_block_data(w,x,y,z)
+*/
+
+/*--------------------------------------------------------------------------*/
+
+static void read_and_print_all_registers( struct stmpe2401 *instance ) {
+ struct i2c_client *client = instance->cl;
+ u8 i,j;
+
+ for( i = 0; i < SZ_STMPE2401_REGMAP; i++ ) {
+ j = read_u8( client, regmap[i].reg );
+ printk( KERN_DEBUG DRIVER_NAME "-regmap[%.2d]: 0x%.2x : 0x%.2x : %s\n",
+ i, regmap[i].reg, j, regmap[i].name );
+ }
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void kpd_work( struct stmpe2401 *instance ) {
+
+// struct stmpe2401 *instance =
+// container_of( work, struct stmpe2401, work[1] );
+
+ struct i2c_client *client = instance->cl;
+ struct stmpe2401_platform_data *pdata = client->dev.platform_data;
+ struct stmpe2401_kpd_data *kpd = pdata->kpd;
+ struct input_dev *input = instance->kpd_input_dev;
+ s32 keycode;
+
+ u8 i,j,k;
+ u8 kpc_data[3] = { 0 };
+ u8 have_key;
+
+ s32 error;
+
+ // an2423 states that the interrupt should be cleared _first_
+ kpc_data[0] = KPC_INT_ST | KPC_FIFO_INT_ST;
+ error = write_u8( client, ISR_lsb, kpc_data[0] );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": %s: failed to clear ISR (error %d)\n",
+ __FUNCTION__, error);
+ }
+
+ have_key = 1;
+ for( j = 0; j < KPC_FIFO_LEN && have_key; j++ ) {
+ for( i = 0; i < 3; i++ ) {
+ error = read_u8( client, KPC_data_byte(i) );
+ if ( error < 0 ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": %s: failed KPC_data_byte%d (error %d)\n",
+ __FUNCTION__, i, error);
+ } else {
+ kpc_data[i] = (u8) error;
+ }
+ }
+#define get_row(x) ((x>>3) & 0xf)
+#define get_col(x) ( x & 0x7)
+#define get_press(x) ( x & 0x80 )
+#define get_dedkey(x,y) ((x>>y) & 0x80)
+ have_key = 0;
+ keycode = 0;
+ for( i = 0; i < 3; i++ ) {
+ if ( i < 2 ) {
+ if ( get_row( kpc_data[i] ) >= 0 &&
+ get_row( kpc_data[i] ) < 12 ) {
+/*
+ printk( KERN_DEBUG DRIVER_NAME ": key %d { %d %d } %s\n",
+ i, get_row( kpc_data[i] ), get_col( kpc_data[i] ),
+ get_press( kpc_data[i] ) ? "up" : "down" );
+*/
+ have_key = 1;
+ if ( instance->key_map_conf == MAP_USE_CB ) {
+ kpd->callback.custom( input, kpc_data[i] );
+ } else {
+ error = input->getkeycode( input,
+ kpc_data[i] & 0x7f, & keycode );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": failed to get keycode for key at "
+ "{ %d %d } (error %d)\n", get_row( kpc_data[i] ),
+ get_col( kpc_data[i] ), error );
+ } else {
+ if ( keycode )
+ input_report_key( input, keycode,
+ get_press( kpc_data[i] ) );
+ }
+ }
+ }
+ } else {
+ if ( pdata->kpd->ded_keys ) {
+ for( k = 0; k < 4; k++ ) {
+ if ( get_dedkey(kpd->ded_keys,k) ) {
+/*
+ printk( KERN_DEBUG DRIVER_NAME
+ ": ded key %d %s\n",
+ k, get_dedkey(kpc_data[i],k) ?
+ "up" : "down" );
+*/
+ have_key = 1;
+ if ( instance->key_map_conf == MAP_USE_CB ) {
+ kpd->callback.custom( input, kpc_data[i] );
+ } else {
+ error = input->getkeycode( input,
+ kpc_data[i] << 8, & keycode );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": failed to get keycode for "
+ "ded key %d (error %d)\n",
+ k, error );
+ } else {
+ if ( keycode )
+ input_report_key( input, keycode,
+ get_press( kpc_data[i] ) );
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+}
+static void pwm_work( struct work_struct *work ) {
+ struct stmpe2401 *instance =
+ container_of( work, struct stmpe2401, work[2] );
+ struct i2c_client *client = instance->cl;
+ mutex_lock( & instance->lock );
+ // TODO: write me
+ mutex_unlock( & instance->lock );
+}
+static void rot_work( struct work_struct *work ) {
+ struct stmpe2401 *instance =
+ container_of( work, struct stmpe2401, work[3] );
+ struct i2c_client *client = instance->cl;
+
+ mutex_lock( & instance->lock );
+ // TODO: write me
+ mutex_unlock( & instance->lock );
+}
+static void gio_work( struct work_struct *work ) {
+ struct stmpe2401 *instance =
+ container_of( work, struct stmpe2401, work[4] );
+ struct i2c_client *client = instance->cl;
+
+ mutex_lock( & instance->lock );
+ // TODO: write me
+ mutex_unlock( & instance->lock );
+}
+static void stmpe2401_work( struct work_struct *work ) {
+ struct stmpe2401 *instance =
+ container_of( work, struct stmpe2401, work[0] );
+ struct i2c_client *client = instance->cl;
+ struct stmpe2401_platform_data *pdata = client->dev.platform_data;
+
+ u8 i, clr;
+ u8 isr[2] = { 0 };
+ u8 conf_data[] = {
+ (pdata->kpd ? 1 : 0),
+ (pdata->pwm ? 1 : 0),
+ (pdata->rot ? 1 : 0),
+ (pdata->gio ? 1 : 0),
+ };
+ // 1 = isr_lsb, 0 = isr_msb
+ u8 mask[][2] = {
+ { 1, KPC_INT_ST | KPC_FIFO_INT_ST },
+ { 1, PWM_CH_INT_ST(3) },
+ { 1, ROT_INT_ST | ROT_BUF_INT_ST },
+ { 0, GPIO_INT_ST },
+ };
+
+ mutex_lock( & instance->lock );
+
+ isr[0] = read_u8( client, ISR_msb );
+ isr[1] = read_u8( client, ISR_lsb );
+/*
+ if ( pdata->kpd && ! instance->kpd_read_once ) {
+ instance->kpd_read_once = 1;
+ isr[1] |= KPC_INT_ST | KPC_FIFO_INT_ST;
+ }
+*/
+
+ for( i = 0; i < 4; i++ ) {
+ if ( isr[ mask[i][0] ] & mask[i][1] ) {
+ if ( conf_data[i] ) {
+ //schedule_work( & instance->work[i] );
+ kpd_work( instance );
+ } else {
+ // anything that gets here is ERRONEOUS - BUG? WARN?
+ }
+ }
+ }
+
+ mutex_unlock( & instance->lock );
+}
+
+/*--------------------------------------------------------------------------*/
+
+static irqreturn_t stmpe2401_interrupt_handler( int irq, void *data ) {
+ struct list_head *pos;
+ struct stmpe2401 *instance = NULL;
+
+ if ( data ) {
+ list_for_each( pos, & instances.list ) {
+ instance = container_of( pos, struct stmpe2401, list );
+ if ( instance == data && instance->cl->irq == irq )
+ break;
+ else
+ instance = NULL;
+ }
+ }
+ if ( ! instance ) {
+ printk( KERN_DEBUG DRIVER_NAME
+ ": no instance matching irq %d at address %p\n",
+ irq, data );
+ return IRQ_NONE;
+ }
+
+ schedule_work( & instance->work[0] );
+
+ return IRQ_HANDLED;
+}
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ conf->gppur_lsb = 0xff;
+ conf->gpdr_msb = 0xff;
+ conf->gpdr_csb = 0xff;
+ conf->gpafr_u_msb = 0x55;
+ conf->gpafr_u_csb = 0x55;
+ conf->gpafr_u_lsb = 0x55;
+ conf->gpafr_l_msb = 0x55;
+ conf->gpafr_l_csb = 0x55;
+ conf->gpafr_l_lsb = 0x55;
+ conf->kpc_col = 0xff;
+ conf->kpc_row_msb = 0xcf;
+ conf->kpc_row_lsb = 0xff;
+ conf->kpc_ctrl_msb = 0x00;
+ conf->icr_lsb = 0x01;
+ conf->ier_lsb = 0x06;
+ conf->syscon = 0x0f;
+ conf->kpc_ctrl_lsb = 0x61;
+*/
+
+static int __init setup_kpd( struct stmpe2401 *instance ) {
+
+ struct stmpe2401_platform_data * pdata =
+ instance->cl->dev.platform_data;
+ struct stmpe2401_kpd_data *kpd = pdata->kpd;
+ struct input_dev *input;
+ struct reg_conf *conf = instance->conf;
+ u8 i,j;
+ int error;
+
+#define bmpx kpd->bmp
+#define rows gio_to_row_bmp( bmpx )
+#define cols gio_to_col_bmp( bmpx )
+
+#define bmp_to_gpdr_msb(x) ((u8)( x >> 16))
+#define bmp_to_gpdr_csb(x) ((u8)( x >> 8))
+#define bmp_to_gpafr(x,y,z) \
+ j = ( x >> z ) & 0xf; \
+ for( i = 0; i < 4; i++ ) { \
+ y |= (j >> i) & 1 ? \
+ 1 << 2*i : 0; \
+ }
+
+ // be bit-friendly with all of the shared registers
+
+ conf->syscon |= KPC_CLK_EN | GPIO_CLK_EN;
+ conf->icr_lsb |= GLOBAL_INT_EN;
+ conf->ier_lsb |= KPC_INT_EN | KPC_FIFO_INT_EN;
+ conf->gpdr_msb |= bmp_to_gpdr_msb( bmpx );
+ conf->gpdr_csb |= bmp_to_gpdr_csb( bmpx );
+ conf->gpdr_lsb &= ~cols;
+ conf->gppur_lsb |= cols;
+
+ bmp_to_gpafr( bmpx, conf->gpafr_u_msb, 20 );
+ bmp_to_gpafr( bmpx, conf->gpafr_u_csb, 16 );
+ bmp_to_gpafr( bmpx, conf->gpafr_u_lsb, 12 );
+ bmp_to_gpafr( bmpx, conf->gpafr_l_msb, 8 );
+ bmp_to_gpafr( bmpx, conf->gpafr_l_csb, 4 );
+ bmp_to_gpafr( bmpx, conf->gpafr_l_lsb, 0 );
+
+
+#define rows_to_kpc_row_msb(x) ((u8)(( x >> 8) & 0xf ))
+#define rows_to_kpc_row_lsb(x) ((u8)( x ))
+
+ // no need to be bit-friendly for keypad-specific registers
+
+ conf->kpc_col = cols;
+ conf->kpc_row_msb = rows_to_kpc_row_msb(rows) | KPC_SCAN_PW;
+ conf->kpc_row_lsb = rows_to_kpc_row_lsb(rows);
+ conf->kpc_ctrl_msb = kpd->ded_keys & 0xf;
+ conf->kpc_ctrl_lsb = KPC_DEBOUNCE_MS(kpd->debounce) | KPC_SCAN_ENABLE;
+
+ // next make an input_dev for the keypad
+
+ instance->kpd_input_dev = input_allocate_device();
+ if ( ! instance->kpd_input_dev ) {
+ error = -ENOMEM;
+ printk( KERN_ERR DRIVER_NAME
+ ": unable to allocate keypad input device (error %d)\n",
+ error );
+ goto err_return;
+ }
+
+ input = instance->kpd_input_dev;
+ input->dev.parent = & instance->cl->dev;
+ input->phys = DRIVER_NAME;
+ input->name = DRIVER_NAME "-keypad";
+ input->id.bustype = BUS_I2C;
+ set_bit( EV_KEY, input->evbit );
+ set_bit( EV_REP, input->evbit );
+ set_bit( EV_MSC, input->evbit );
+ set_bit( MSC_SCAN, input->mscbit );
+ input->keycodesize = STMPE2401_KEYMAP_SCANCODESIZE;
+
+ switch( instance->key_map_conf ) {
+ case MAP_USE_CB:
+ input->setkeycode = kpd->callback.setkeycode;
+ for( i = 0; i < kpd->keymap.keycodemax; i++ )
+ set_bit( kpd->keymap.keycode[i], input->keybit );
+ break;
+ case MAP_USE_KM_WITH_S:
+ input->setkeycode = kpd->callback.setkeycode;
+ case MAP_USE_KM:
+ input->keycode = kpd->keymap.keycode;
+ input->keycodemax = kpd->keymap.keycodemax;
+ for( i = 0; i < kpd->keymap.keycodemax; i++ )
+ set_bit( i, input->keybit );
+ break;
+ default:
+ // this is a BUG ... validation is done beforehand, so code should
never get here.
+ printk( KERN_ERR DRIVER_NAME
+ ": invalid keypad mapping configuration 0x%.2x\n",
+ instance->key_map_conf );
+ error = -EINVAL;
+ goto free_input;
+ }
+
+ error = input_register_device( input );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME ": failed to register keypad input
device (error %d)\n", error );
+ goto free_input;
+ }
+
+free_input:
+ input_free_device( input );
+err_return:
+ return 0;
+}
+static int __init setup_pwm( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+static int __init setup_rot( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+static int __init setup_gio( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+
+static void __init cleanup_kpd( struct stmpe2401 *instance ) {
+ struct input_dev *input = instance->kpd_input_dev;
+
+ printk( KERN_DEBUG DRIVER_NAME ": locking stmp2401 instance\n" );
+
+ if ( input ) {
+ printk( KERN_DEBUG DRIVER_NAME ": unregistering keypad input device\n" );
+ input_unregister_device( input );
+ //cancel_work_sync( & instance->work[1] );
+ instance->kpd_input_dev = NULL;
+ }
+}
+static void __init cleanup_pwm( struct stmpe2401 *instance ) {
+
+}
+static void __init cleanup_rot( struct stmpe2401 *instance ) {
+
+}
+static void __init cleanup_gio( struct stmpe2401 *instance ) {
+
+}
+
+/*--------------------------------------------------------------------------*/
+
+static void __init init_config( struct reg_conf *conf ) {
+ conf->syscon = RESET_VAL(SYSCON);
+ conf->icr_msb = RESET_VAL(ICR_msb);
+ conf->icr_lsb = RESET_VAL(ICR_lsb);
+ conf->ier_msb = RESET_VAL(IER_msb);
+ conf->ier_lsb = RESET_VAL(IER_lsb);
+ conf->isr_msb = RESET_VAL(ISR_msb);
+ conf->isr_lsb = RESET_VAL(ISR_lsb);
+ conf->iegpior_msb = RESET_VAL(IEGPIOR_msb);
+ conf->iegpior_csb = RESET_VAL(IEGPIOR_csb);
+ conf->iegpior_lsb = RESET_VAL(IEGPIOR_lsb);
+ conf->isgpior_msb = RESET_VAL(ISGPIOR_msb);
+ conf->isgpior_csb = RESET_VAL(ISGPIOR_csb);
+ conf->isgpior_lsb = RESET_VAL(ISGPIOR_lsb);
+ conf->pwmcs = RESET_VAL(PWMCS);
+ conf->pwmic0 = RESET_VAL(PWMIC0);
+ conf->pwmic1 = RESET_VAL(PWMIC1);
+ conf->pwmic2 = RESET_VAL(PWMIC2);
+ conf->kpc_col = RESET_VAL(KPC_col);
+ conf->kpc_row_msb = RESET_VAL(KPC_row_msb);
+ conf->kpc_row_lsb = RESET_VAL(KPC_row_lsb);
+ conf->kpc_ctrl_msb = RESET_VAL(KPC_ctrl_msb);
+ conf->kpc_ctrl_lsb = RESET_VAL(KPC_ctrl_lsb);
+ conf->kpc_data_byte0 = RESET_VAL(KPC_data_byte0);
+ conf->kpc_data_byte1 = RESET_VAL(KPC_data_byte1);
+ conf->kpc_data_byte2 = RESET_VAL(KPC_data_byte2);
+ conf->chip_id = RESET_VAL(CHIP_ID);
+ conf->version_id = RESET_VAL(VERSION_ID);
+ conf->gpsr_msb = RESET_VAL(GPSR_msb);
+ conf->gpsr_csb = RESET_VAL(GPSR_csb);
+ conf->gpsr_lsb = RESET_VAL(GPSR_lsb);
+ conf->gpcr_msb = RESET_VAL(GPCR_msb);
+ conf->gpcr_csb = RESET_VAL(GPCR_csb);
+ conf->gpcr_lsb = RESET_VAL(GPCR_lsb);
+ conf->gpdr_msb = RESET_VAL(GPDR_msb);
+ conf->gpdr_csb = RESET_VAL(GPDR_csb);
+ conf->gpdr_lsb = RESET_VAL(GPDR_lsb);
+ conf->gpedr_msb = RESET_VAL(GPEDR_msb);
+ conf->gpedr_csb = RESET_VAL(GPEDR_csb);
+ conf->gpedr_lsb = RESET_VAL(GPEDR_lsb);
+ conf->gprer_msb = RESET_VAL(GPRER_msb);
+ conf->gprer_csb = RESET_VAL(GPRER_csb);
+ conf->gprer_lsb = RESET_VAL(GPRER_lsb);
+ conf->gpfer_msb = RESET_VAL(GPFER_msb);
+ conf->gpfer_csb = RESET_VAL(GPFER_csb);
+ conf->gpfer_lsb = RESET_VAL(GPFER_lsb);
+ conf->gppur_msb = RESET_VAL(GPPUR_msb);
+ conf->gppur_csb = RESET_VAL(GPPUR_csb);
+ conf->gppur_lsb = RESET_VAL(GPPUR_lsb);
+ conf->gppdr_msb = RESET_VAL(GPPDR_msb);
+ conf->gppdr_csb = RESET_VAL(GPPDR_csb);
+ conf->gppdr_lsb = RESET_VAL(GPPDR_lsb);
+ conf->gpafr_u_msb = RESET_VAL(GPAFR_U_msb);
+ conf->gpafr_u_csb = RESET_VAL(GPAFR_U_csb);
+ conf->gpafr_u_lsb = RESET_VAL(GPAFR_U_lsb);
+ conf->gpafr_l_msb = RESET_VAL(GPAFR_L_msb);
+ conf->gpafr_l_csb = RESET_VAL(GPAFR_L_csb);
+ conf->gpafr_l_lsb = RESET_VAL(GPAFR_L_lsb);
+ conf->gpmr_msb = RESET_VAL(GPMR_msb);
+ conf->gpmr_csb = RESET_VAL(GPMR_csb);
+ conf->gpmr_lsb = RESET_VAL(GPMR_lsb);
+
+ // syscon should be 0 to begin with
+ conf->syscon = 0;
+ // all unused gpio should be output and set to zero
+ // to begin with
+ conf->gpdr_msb = 0xff;
+ conf->gpdr_csb = 0xff;
+ conf->gpdr_lsb = 0xff;
+}
+static int __init write_config( struct stmpe2401 *instance ) {
+ struct i2c_client *client = instance->cl;
+ struct reg_conf *conf = instance->conf;
+ int error = 0;
+
+#define WRITE_ORDER(x) \
+ { x->gppur_msb, RESET_VAL_GPPUR_msb, GPPUR_msb }, \
+ { x->gppur_csb, RESET_VAL_GPPUR_csb, GPPUR_csb }, \
+ { x->gppur_lsb, RESET_VAL_GPPUR_lsb, GPPUR_lsb }, \
+ { x->gppdr_msb, RESET_VAL_GPPDR_msb, GPPDR_msb }, \
+ { x->gppdr_csb, RESET_VAL_GPPDR_csb, GPPDR_csb }, \
+ { x->gppdr_lsb, RESET_VAL_GPPDR_lsb, GPPDR_lsb }, \
+ { x->gpdr_msb, RESET_VAL_GPDR_msb, GPDR_msb }, \
+ { x->gpdr_csb, RESET_VAL_GPDR_csb, GPDR_csb }, \
+ { x->gpdr_lsb, RESET_VAL_GPDR_lsb, GPDR_lsb }, \
+ { x->gpafr_u_msb, RESET_VAL_GPAFR_U_msb, GPAFR_U_msb }, \
+ { x->gpafr_u_csb, RESET_VAL_GPAFR_U_csb, GPAFR_U_csb }, \
+ { x->gpafr_u_lsb, RESET_VAL_GPAFR_U_lsb, GPAFR_U_lsb }, \
+ { x->gpafr_l_msb, RESET_VAL_GPAFR_L_msb, GPAFR_L_msb }, \
+ { x->gpafr_l_csb, RESET_VAL_GPAFR_L_csb, GPAFR_L_csb }, \
+ { x->gpafr_l_lsb, RESET_VAL_GPAFR_L_lsb, GPAFR_L_lsb }, \
+ { x->kpc_col, RESET_VAL_KPC_col, KPC_col }, \
+ { x->kpc_row_msb, RESET_VAL_KPC_row_msb, KPC_row_msb }, \
+ { x->kpc_row_lsb, RESET_VAL_KPC_row_lsb, KPC_row_lsb }, \
+ { x->kpc_ctrl_msb, RESET_VAL_KPC_ctrl_msb, KPC_ctrl_msb }, \
+ { x->iegpior_msb, RESET_VAL_IEGPIOR_msb, IEGPIOR_msb }, \
+ { x->iegpior_csb, RESET_VAL_IEGPIOR_csb, IEGPIOR_csb }, \
+ { x->iegpior_lsb, RESET_VAL_IEGPIOR_lsb, IEGPIOR_lsb }, \
+ { x->isgpior_msb, RESET_VAL_ISGPIOR_msb, ISGPIOR_msb }, \
+ { x->isgpior_csb, RESET_VAL_ISGPIOR_csb, ISGPIOR_csb }, \
+ { x->isgpior_lsb, RESET_VAL_ISGPIOR_lsb, ISGPIOR_lsb }, \
+ { x->gpedr_msb, RESET_VAL_GPEDR_msb, GPEDR_msb }, \
+ { x->gpedr_csb, RESET_VAL_GPEDR_csb, GPEDR_csb }, \
+ { x->gpedr_lsb, RESET_VAL_GPEDR_lsb, GPEDR_lsb }, \
+ { x->gprer_msb, RESET_VAL_GPRER_msb, GPRER_msb }, \
+ { x->gprer_csb, RESET_VAL_GPRER_csb, GPRER_csb }, \
+ { x->gprer_lsb, RESET_VAL_GPRER_lsb, GPRER_lsb }, \
+ { x->gpfer_msb, RESET_VAL_GPFER_msb, GPFER_msb }, \
+ { x->gpfer_csb, RESET_VAL_GPFER_csb, GPFER_csb }, \
+ { x->gpfer_lsb, RESET_VAL_GPFER_lsb, GPFER_lsb }, \
+ { x->gpsr_msb, RESET_VAL_GPSR_msb, GPSR_msb }, \
+ { x->gpsr_csb, RESET_VAL_GPSR_csb, GPSR_csb }, \
+ { x->gpsr_lsb, RESET_VAL_GPSR_lsb, GPSR_lsb }, \
+ { x->gpcr_msb, RESET_VAL_GPCR_msb, GPCR_msb }, \
+ { x->gpcr_csb, RESET_VAL_GPCR_csb, GPCR_csb }, \
+ { x->gpcr_lsb, RESET_VAL_GPCR_lsb, GPCR_lsb }, \
+ { x->pwmcs, RESET_VAL_PWMCS, PWMCS }, \
+ { x->pwmic0, RESET_VAL_PWMIC0, PWMIC0 }, \
+ { x->pwmic1, RESET_VAL_PWMIC1, PWMIC1 }, \
+ { x->pwmic2, RESET_VAL_PWMIC2, PWMIC2 }, \
+ { x->icr_lsb, RESET_VAL_ICR_lsb, ICR_lsb }, \
+ { x->ier_msb, RESET_VAL_IER_msb, IER_msb }, \
+ { x->ier_lsb, RESET_VAL_IER_lsb, IER_lsb }, \
+ { x->syscon, RESET_VAL_SYSCON, SYSCON }, \
+ { x->kpc_ctrl_lsb, RESET_VAL_KPC_ctrl_lsb, KPC_ctrl_lsb },
+
+ u8 set[60][3] = { WRITE_ORDER(conf) };
+ u8 i;
+
+ for ( i = 0; i < 60; i++ )
+ if ( set[i][0] != set[i][1] ) {
+// printk( KERN_DEBUG DRIVER_NAME ": writing 0x%.2x to 0x%.2x\n",
+// set[i][0], set[i][2] );
+ error = write_u8( client, set[i][2], set[i][0] );
+ if ( error ) {
+ printk( KERN_DEBUG DRIVER_NAME
+ ": failed to write 0x%.2x to 0x%.2x\n",
+ set[i][0], set[i][2] );
+ break;
+ }
+ }
+
+ return error;
+}
+
+static int __init configure_stmpe2401(
+ struct stmpe2401 *instance ) {
+
+ struct stmpe2401_platform_data *pdata = instance->cl->dev.platform_data;
+ u8 conf_data[] = {
+ (pdata->kpd ? 1 : 0),
+ (pdata->pwm ? 1 : 0),
+ (pdata->rot ? 1 : 0),
+ (pdata->gio ? 1 : 0),
+ };
+ int (*setup[])( struct stmpe2401 *instance )
+ = { & setup_kpd, & setup_pwm, & setup_rot, & setup_gio };
+ void (*cleanup[])( struct stmpe2401 *instance )
+ = { & cleanup_kpd, & cleanup_pwm, & cleanup_rot, & cleanup_gio };
+// void (*work_fn[])( struct work_struct *work )
+// = { & kpd_work, & pwm_work, & rot_work, & gio_work };
+
+ u8 i;
+ int error = 0;
+
+ INIT_WORK( & instance->work[0], stmpe2401_work );
+ mutex_init( & instance->lock );
+
+ instance->conf = kzalloc( sizeof( struct reg_conf) , GFP_KERNEL );
+ if ( ! instance->conf ) { error = -ENOMEM; goto err_return; }
+ init_config( instance->conf );
+
+ for( i = 0; i < 4; i++ ) {
+ if ( conf_data[i] ) {
+ //INIT_WORK( & instance->work[i+1], work_fn[i] );
+ error = setup[i]( instance );
+ if ( error ) goto free_stuff;
+ }
+ }
+
+ error = write_config( instance );
+ if ( error ) goto free_conf;
+
+ error = 0;
+
+ kfree( instance->conf );
+ return 0;
+free_stuff:
+ for(; i >= 0; i-- )
+ if ( conf_data[i] )
+ cleanup[i]( instance );
+free_conf:
+ kfree( instance->conf );
+err_return:
+ return error;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static int __init read_ids( struct i2c_client *client ) {
+ u8 chip_id, version_id;
+ chip_id = read_u8( client, CHIP_ID );
+ version_id = read_u8( client, VERSION_ID );
+ if ( chip_id != RESET_VAL_CHIP_ID ||
+ version_id != RESET_VAL_VERSION_ID ) {
+ printk( KERN_ERR DRIVER_NAME ": stmpe2401 device not found\n" );
+ return -ENODEV;
+ }
+ printk( KERN_DEBUG DRIVER_NAME
+ ": stmpe2401 found with CHIP_ID %x and VERSION_ID %x\n",
+ chip_id, version_id );
+
+ return 0;
+}
+static int __init pin_conflict( struct stmpe2401_platform_data * pdata ) {
+
+#define bmp_kpd pdata->kpd ? (u32)pdata->kpd->bmp : 0
+#define bmp_pwm pdata->pwm ? (u32)pdata->pwm->bmp : 0
+#define bmp_rot pdata->rot ? (u32)pdata->rot->bmp : 0
+#define bmp_gio pdata->gio ? (u32)pdata->gio->bmp : 0
+#define bmp(x) bmp_##x
+#define bmp_nc pdata->nc
+
+ u32 conflict = 0;
+ u32 bmp[5] = { bmp_nc, bmp(kpd), bmp(pwm), bmp(rot), bmp(gio) };
+ u8 i;
+
+ for ( i = 0; i < sizeof(bmp); i++ ) {
+ conflict &= bmp[i];
+ if ( conflict ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": pin assignment conflict 0x%x\n", conflict );
+ break;
+ }
+ }
+ return conflict;
+}
+
+static int __init validate_kpd( struct stmpe2401 *instance ) {
+
+ struct stmpe2401_platform_data *pdata = instance->cl->dev.platform_data;
+ struct stmpe2401_kpd_data *kpd = pdata->kpd;
+ int error;
+
+#define map_cb_c(x) \
+ ( x->callback.custom ? 1 : 0)
+#define map_cb_s(x) \
+ ( x->callback.setkeycode ? 1 : 0)
+#define map_km_kc(x) \
+ (x->keymap.keycode ? 1 : 0)
+#define map_km_kcm(x) \
+ ((x->keymap.keycodemax > 0 && x->keymap.keycodemax < KEY_CNT) ? 1 : 0)
+#define map_bit(x,y) \
+ (x<<y)
+#define map_conf(x) \
+ (u8)( map_bit( map_cb_c(x),3) | \
+ map_bit( map_cb_s(x),2) | \
+ map_bit( map_km_kc(x),1) | \
+ map_bit( map_km_kcm(x),0) )
+
+ instance->key_map_conf = map_conf( kpd );
+ switch( instance->key_map_conf ) {
+ case MAP_USE_CB:
+ case MAP_USE_KM_WITH_S:
+ case MAP_USE_KM:
+ break;
+ default:
+ printk( KERN_ERR DRIVER_NAME
+ ": invalid keymap configuration (0x%x)\n", instance->key_map_conf );
+ error = -EINVAL;
+ goto err_return;
+ }
+
+ return 0;
+
+err_return:
+ return error;
+}
+static int __init validate_pwm( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+static int __init validate_rot( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+static int __init validate_gio( struct stmpe2401 *instance ) {
+ return -ENOSYS;
+}
+static int __init validate_stmpe2401( struct stmpe2401 *instance ) {
+ struct stmpe2401_platform_data *pdata = instance->cl->dev.platform_data;
+ struct i2c_client *client = instance->cl;
+ int error;
+ u8 i;
+
+ int (*validate[])(struct stmpe2401 *instance) =
+ { & validate_kpd, & validate_pwm, & validate_rot, & validate_gio };
+
+ u8 conf_data[] = {
+ (pdata->kpd ? 1 : 0), (pdata->pwm ? 1 : 0),
+ (pdata->rot ? 1 : 0), (pdata->gio ? 1 : 0) };
+
+ u8 has_conf_data = 0;
+ for( i = 0; i < 4; i++ ) {
+ has_conf_data |= conf_data[i];
+ }
+ if( ! has_conf_data ) {
+ printk( KERN_ERR DRIVER_NAME ": no configuration in platform data\n" );
+ error = -EINVAL;
+ goto err_return;
+ }
+
+ error = pin_conflict( pdata );
+ if ( error ) {
+ error = -EINVAL;
+ goto err_return;
+ }
+
+ error = read_ids( client );
+ if ( error ) goto err_return;
+
+ for( i = 0; i < 4; i++ ) {
+ if ( conf_data[i] ) {
+ error = validate[i]( instance );
+ if ( error ) goto err_return;
+ }
+ }
+
+ return 0;
+
+err_return:
+ return error;
+}
+/*--------------------------------------------------------------------------*/
+
+
+/*--------------------------------------------------------------------------*/
+
+static int __devinit
+stmpe2401_probe(
+ struct i2c_client *client, const struct i2c_device_id *id ) {
+
+ struct stmpe2401_platform_data *pdata = client->dev.platform_data;
+ int error = -EINVAL;
+ struct stmpe2401 * instance;
+ int sw_reset = 0;
+
+ if ( ! ( client && client->dev.platform_data ) ) {
+ printk( KERN_ERR DRIVER_NAME ": no platform data\n");
+ error = -EINVAL;
+ goto err_return;
+ }
+
+ instance = kzalloc( sizeof( struct stmpe2401 ), GFP_KERNEL );
+ if ( ! instance ) {
+ printk( KERN_ERR DRIVER_NAME ": could not allocate stmpe2401 struct\n" );
+ error = -ENOMEM;
+ goto err_return;
+ }
+
+ INIT_LIST_HEAD( & instance->list );
+ list_add_tail( & instance->list, & instances.list );
+ instance->cl = client;
+
+ error = validate_stmpe2401( instance );
+ if ( error ) {
+ printk( KERN_ERR "failed to validate stmpe2401 (error %d)\n", error );
+ goto remove_from_list;
+ }
+
+ if ( pdata->getpower && pdata->setpower )
+ if ( ! pdata->getpower() )
+ pdata->setpower( 1 );
+ else
+ if ( pdata->reset )
+ pdata->reset();
+ else
+ sw_reset = 1;
+ else
+ sw_reset = 1;
+
+ if ( sw_reset ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": using soft-reset because hardware reset is unavailable.\n" );
+ error = write_u8( client, SYSCON, RESET );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": failed to send reset signal to stmpe2401 (error %d)\n", error );
+ goto free_instance;
+ }
+ }
+
+ // IRQF_DISABLED ?
+ error = request_irq( client->irq, stmpe2401_interrupt_handler,
+ IRQF_TRIGGER_FALLING | IRQF_TRIGGER_RISING, DRIVER_NAME,
+ instance );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": unable to request irq %d (error %d)\n",
+ client->irq, error );
+ goto free_instance;
+ }
+
+ error = configure_stmpe2401( instance );
+ if ( error ) {
+ printk( KERN_ERR DRIVER_NAME
+ ": failed to configure stmpe2401 (error %d)\n", error );
+ goto free_irq;
+ }
+
+ printk( KERN_INFO DRIVER_NAME ": added stmpe2401 with irq %d at i2c %d-%x\n",
+ client->irq, client->adapter->nr, client->addr );
+
+ return 0;
+
+free_irq:
+ free_irq( client->irq, instance );
+ write_u8( client, SYSCON, RESET );
+remove_from_list:
+ list_del( & instance->list );
+free_instance:
+ kfree( instance );
+err_return:
+ return error;
+}
+
+static int __devexit
+stmpe2401_remove( struct i2c_client *client ) {
+ struct stmpe2401 *instance = NULL;
+ struct stmpe2401_platform_data *pdata;
+ struct list_head *pos;
+ void (*cleanup[4])( struct stmpe2401 *instance );
+ int i = 0;
+
+ list_for_each( pos, & instances.list ) {
+ instance = container_of( pos, struct stmpe2401, list );
+ if ( instance->cl == client )
+ break;
+ instance = NULL;
+ i++;
+ }
+ if ( ! instance ) {
+ printk( KERN_DEBUG DRIVER_NAME ": cannot remove i2c_client %p: "
+ "not found in list!\n", client );
+ return -EINVAL;
+ }
+
+ pdata = instance->cl->dev.platform_data;
+
+ cleanup[0] = & cleanup_kpd;
+ cleanup[1] = & cleanup_pwm;
+ cleanup[2] = & cleanup_rot;
+ cleanup[3] = & cleanup_gio;
+/*
+ for( i = 3; i >=0; i-- ) {
+ cleanup[i]( instance );
+ }
+*/
+
+// from lm8323.c
+// disable_irq_wake(client->irq);
+// free_irq(client->irq, lm);
+// cancel_work_sync(&lm->work);
+
+
+ free_irq( instance->cl->irq, instance );
+ write_u8( instance->cl, SYSCON, RESET );
+
+ list_del( & instance->list );
+ kfree( instance );
+
+ printk( KERN_INFO DRIVER_NAME ": removed stmpe2401 from i2c %d-%x\n",
+ client->adapter->nr, client->addr );
+
+ return 0;
+}
+
+/*--------------------------------------------------------------------------*/
+
+static const unsigned short normal_i2c[] =
+{ 0x42, 0x43, 0x44, 0x45, I2C_CLIENT_END };
+
+static const struct i2c_client_address_data stmpe2401_addr_data = {
+ .normal_i2c = normal_i2c,
+};
+
+static const struct i2c_device_id stmpe2401_ids[] = {
+ { DRIVER_NAME, 0 },
+ { }
+};
+
+/*--------------------------------------------------------------------------*/
+
+static struct i2c_driver stmpe2401_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ .owner = THIS_MODULE,
+ },
+ .probe = stmpe2401_probe,
+ .remove = stmpe2401_remove,
+ .id_table = stmpe2401_ids,
+ .address_data = & stmpe2401_addr_data,
+};
+
+/*--------------------------------------------------------------------------*/
+
+static int __init stmpe2401_init(void) {
+ return i2c_add_driver( &stmpe2401_driver );
+}
+
+static void __exit stmpe2401_exit(void) {
+ i2c_del_driver( &stmpe2401_driver );
+}
+
+/*--------------------------------------------------------------------------*/
+
+subsys_initcall( stmpe2401_init );
+module_exit( stmpe2401_exit );
+
+/*--------------------------------------------------------------------------*/
+
+MODULE_DEVICE_TABLE( i2c, stmpe2401_ids );
+MODULE_LICENSE( "GPL" );
+MODULE_DESCRIPTION( "stmpe2401 GPIO port expander from ST Micro" );
+MODULE_AUTHOR( "Christopher Friedt, <chrisfriedt@gmail.com>" );
+
+/*
+ * To Do List
+ * ==========
+ *
+ * =======
+ * General
+ * =======
+ * TODO: somehow seperate this into seperate modules, mfd style
+ *
+ * ====
+ * GPIO
+ * ====
+ * TODO: create a gpio_chip for the configured gpio lines
+ *
+ * =============
+ * Rotator Input
+ * =============
+ * TODO: impliment rotator handling, input device, etc
+ *
+ * ==============
+ * PWM Controller
+ * ==============
+ * TODO: Write command / IOCTL interface for PWM controller
+ *
+ * =======================
+ * Power Management Module
+ * =======================
+ * TODO: handle power management
+ *
+ */
new file mode 100644
@@ -0,0 +1,500 @@
+#ifndef __LINUX_I2C_STMPE2401_H
+#define __LINUX_I2C_STMPE2401_H
+
+#include <linux/input.h>
+
+/**
+ * struct stmpe2401_platform_data - data to set up stmpe2401 driver
+ * @gpio_base: number of the chip's first GPIO
+ * @setup: optional callback issued once the GPIOs are valid
+ * @teardown: optional callback issued before the GPIOs are invalidated
+ * @context: optional parameter passed to setup() and teardown()
+ *
+ * In addition to the I2C_BOARD_INFO() state appropriate to each chip,
+ * the i2c_board_info used with the stmpe2401 driver must provide its
+ * platform_data (pointer to one of these structures) with at least
+ * the gpio_base value initialized.
+ *
+ * The @setup callback may be used with the kind of board-specific glue
+ * which hands the (now-valid) GPIOs to other drivers, or which puts
+ * devices in their initial states using these GPIOs.
+ */
+
+#define MAX_GPIO 24
+#define MAX_ROWS 12
+#define MAX_COLS 8
+#define MAX_PWM 3
+
+#define KPC_FIFO_LEN 4
+
+#define BASE 0x00
+
+/* Identification Registers */
+#define CHIP_ID (BASE + 0x80)
+#define VERSION_ID (BASE + 0x81)
+/* IDR Read Values */
+#define RESET_VAL_CHIP_ID 0x01
+#define RESET_VAL_VERSION_ID 0x01
+
+/* System Control Register */
+#define SYSCON (BASE + 0x02)
+/* SCR Write Values */
+#define ROT_CLK_EN ( 1 << 0 )
+#define KPC_CLK_EN ( 1 << 1 )
+#define PWM_CLK_EN ( 1 << 2 )
+#define GPIO_CLK_EN ( 1 << 3 )
+#define SLEEP ( 1 << 4 )
+#define DISABLE_32KHZ ( 1 << 5 )
+#define RESET ( 1 << 7 )
+#define SYSCON_SET(x) ( 1 << x )
+#define RESET_VAL_SYSCON 0x0f
+
+/* Interrupt Control Register */
+#define ICR_msb (BASE + 0x10)
+#define ICR_lsb (BASE + 0x11)
+/* ICR Write Values
+ * Note: edge-type output interrupts produce only a microscopic pulse
+ * It is suggested to use level-type output interrupts */
+#define GLOBAL_INT_EN ( 1 << 0 )
+#define EDGE_INT_OUT ( 1 << 1 ) // output edge interrupt signals
+#define LEVEL_INT_OUT ( 0 << 1 ) // output level interrupts signals
+#define RISING_EDGE_OUT ( 1 << 2 ) // output falling edge interrupt
+#define FALLING_EDGE_OUT (0 << 2 ) // output rising edge interrupt
+#define HIGH_LEVEL_OUT ( 1 << 2 ) // active-high level interrupt
+#define LOW_LEVEL_OUT ( 0 << 2 ) // active-low level interrupt
+#define ICR_SET(x) ( 1 << x )
+#define RESET_VAL_ICR_msb 0x00
+#define RESET_VAL_ICR_lsb 0x00
+
+/* Interrupt Enable Register */
+#define IER_msb (BASE + 0x12)
+#define IER_lsb (BASE + 0x13)
+/* ICR Write Values */
+#define WAKEUP_EN ( 1 << 0 )
+#define KPC_INT_EN ( 1 << 1 )
+#define KPC_FIFO_INT_EN ( 1 << 2 ) // enable fifo overflow interrupt
+#define ROT_INT_EN ( 1 << 3 )
+#define ROT_BUF_INT_EN ( 1 << 4 ) // enable buffer overflow interrupt
+#define PWM_CH0_INT_EN ( 1 << 5 )
+#define PWM_CH1_INT_EN ( 1 << 6 )
+#define PWM_CH2_INT_EN ( 1 << 7 )
+#define PWM_CH_INT_EN(x) (x << 5 )
+#define GPIO_INT_EN ( 1 << 8 )
+#define IER_SET(x) ( 1 << x )
+#define IER_CLEAR(x,y) ( x & ~(1 << y))
+#define RESET_VAL_IER_msb 0x00
+#define RESET_VAL_IER_lsb 0x00
+
+/* Interrupt Status Register */
+#define ISR_msb (BASE + 0x14)
+#define ISR_lsb (BASE + 0x15)
+/* ICR Write Values */
+#define WAKEUP_ST ( 1 << 0 )
+#define KPC_INT_ST ( 1 << 1 )
+#define KPC_FIFO_INT_ST ( 1 << 2 )
+#define ROT_INT_ST ( 1 << 3 )
+#define ROT_BUF_INT_ST ( 1 << 4 )
+#define PWM_CH0_INT_ST ( 1 << 5 )
+#define PWM_CH1_INT_ST ( 1 << 6 )
+#define PWM_CH2_INT_ST ( 1 << 7 )
+#define PWM_CH_INT_ST(x) (x << 5 )
+#define GPIO_INT_ST ( 1 << 0 )
+#define ISR_SET(x) ( 1 << x )
+#define ISR_CLEAR(x,y) ( ~x & (1 << y))
+#define RESET_VAL_ISR_msb 0x00
+#define RESET_VAL_ISR_lsb 0x00
+
+/* IER / ISR Read Values */
+#define WAKEUP 0
+#define KPC_INT 1
+#define KPC_FIFO_INT 2
+#define ROT_INT 3
+#define ROT_BUF_INT 4
+#define PWM_CH(x) (5+x) // valid for x values of 0-2
+#define GPIO_INT 8
+#define IER_GET(x,y) ((x >> y) & 0x1)
+#define ISR_GET(x,y) IER_GET(x,y)
+
+/* Interrupt Enable GPIO Mask Register */
+#define IEGPIOR_msb (BASE + 0x16)
+#define IEGPIOR_csb (BASE + 0x17)
+#define IEGPIOR_lsb (BASE + 0x18)
+#define RESET_VAL_IEGPIOR_msb 0x00
+#define RESET_VAL_IEGPIOR_csb 0x00
+#define RESET_VAL_IEGPIOR_lsb 0x00
+
+/* Interrupt Status GPIO Register */
+#define ISGPIOR_msb (BASE + 0x19)
+#define ISGPIOR_csb (BASE + 0x1A)
+#define ISGPIOR_lsb (BASE + 0x1B)
+#define RESET_VAL_ISGPIOR_msb 0x00
+#define RESET_VAL_ISGPIOR_csb 0x00
+#define RESET_VAL_ISGPIOR_lsb 0x00
+
+/* GPIO Monitor Pin State Register */
+#define GPMR_msb (BASE + 0xA2)
+#define GPMR_csb (BASE + 0xA3)
+#define GPMR_lsb (BASE + 0xA4)
+#define RESET_VAL_GPMR_msb 0x00
+#define RESET_VAL_GPMR_csb 0x00
+#define RESET_VAL_GPMR_lsb 0x00
+
+/* GPIO Set Pin State Register */
+#define GPSR_msb (BASE + 0x83)
+#define GPSR_csb (BASE + 0x84)
+#define GPSR_lsb (BASE + 0x85)
+#define RESET_VAL_GPSR_msb 0x00
+#define RESET_VAL_GPSR_csb 0x00
+#define RESET_VAL_GPSR_lsb 0x00
+
+/* GPIO Clear Pin State Register */
+#define GPCR_msb (BASE + 0x86)
+#define GPCR_csb (BASE + 0x87)
+#define GPCR_lsb (BASE + 0x88)
+#define RESET_VAL_GPCR_msb 0x00
+#define RESET_VAL_GPCR_csb 0x00
+#define RESET_VAL_GPCR_lsb 0x00
+
+/* GPIO Set Pin Direction Register */
+#define GPDR_msb (BASE + 0x89)
+#define GPDR_csb (BASE + 0x8A)
+#define GPDR_lsb (BASE + 0x8B)
+#define RESET_VAL_GPDR_msb 0x00
+#define RESET_VAL_GPDR_csb 0x00
+#define RESET_VAL_GPDR_lsb 0x00
+
+/* GPIO Edge Detect Pin Status Register */
+#define GPEDR_msb (BASE + 0x8C)
+#define GPEDR_csb (BASE + 0x8D)
+#define GPEDR_lsb (BASE + 0x8E)
+#define RESET_VAL_GPEDR_msb 0x00
+#define RESET_VAL_GPEDR_csb 0x00
+#define RESET_VAL_GPEDR_lsb 0x00
+
+/* GPIO Rising Edge Register */
+#define GPRER_msb (BASE + 0x8F)
+#define GPRER_csb (BASE + 0x90)
+#define GPRER_lsb (BASE + 0x91)
+#define RESET_VAL_GPRER_msb 0x00
+#define RESET_VAL_GPRER_csb 0x00
+#define RESET_VAL_GPRER_lsb 0x00
+
+/* GPIO Falling Edge Register */
+#define GPFER_msb (BASE + 0x92)
+#define GPFER_csb (BASE + 0x93)
+#define GPFER_lsb (BASE + 0x94)
+#define RESET_VAL_GPFER_msb 0x00
+#define RESET_VAL_GPFER_csb 0x00
+#define RESET_VAL_GPFER_lsb 0x00
+
+/* GPIO Pull-Up Register */
+#define GPPUR_msb (BASE + 0x95)
+#define GPPUR_csb (BASE + 0x96)
+#define GPPUR_lsb (BASE + 0x97)
+#define RESET_VAL_GPPUR_msb 0x00
+#define RESET_VAL_GPPUR_csb 0x00
+#define RESET_VAL_GPPUR_lsb 0x00
+
+/* GPIO Pull-Down Register */
+#define GPPDR_msb (BASE + 0x98)
+#define GPPDR_csb (BASE + 0x99)
+#define GPPDR_lsb (BASE + 0x9A)
+#define RESET_VAL_GPPDR_msb 0x00
+#define RESET_VAL_GPPDR_csb 0x00
+#define RESET_VAL_GPPDR_lsb 0x00
+
+/* GPIO Alternate-Function Register (Upper Bit) */
+#define GPAFR_U_msb (BASE + 0x9B)
+#define GPAFR_U_csb (BASE + 0x9C)
+#define GPAFR_U_lsb (BASE + 0x9D)
+/* GPIO Alternate-Function Register (Lower Bit) */
+#define GPAFR_L_msb (BASE + 0x9E)
+#define GPAFR_L_csb (BASE + 0x9F)
+#define GPAFR_L_lsb (BASE + 0xA0)
+/* GPAFR Write Values */
+#define KPD_AFR_PATTERN ((u8)0x01)
+#define PWM_AFR_PATTERN ((u8)0x00)
+#define ROT_AFR_PATTERN ((u8)0x01)
+#define GIO_AFR_PATTERN ((u8)0x00)
+#define RESET_VAL_GPAFR_L_msb 0x00
+#define RESET_VAL_GPAFR_L_csb 0x00
+#define RESET_VAL_GPAFR_L_lsb 0x00
+#define RESET_VAL_GPAFR_U_msb 0x00
+#define RESET_VAL_GPAFR_U_csb 0x00
+#define RESET_VAL_GPAFR_U_lsb 0x00
+
+#define GPIO_TO_BMP(x) (1 << x)
+#define GPIO_PWM(x) (21 + x)
+#define GPIO_ADDR0 15
+#define GPIO_ROT(x) (18 + x)
+#define GPIO_KP_X(x) x
+#define GPIO_KP_Y(x) ( (x < 7) ? (x+8) : (x+9) )
+
+/* PWM Control and Status Register */
+#define PWMCS (BASE + 0x30)
+#define PWMIC0 (BASE + 0x38)
+#define PWMIC1 (BASE + 0x39)
+#define PWMIC2 (BASE + 0x3A)
+/* PWMCR Write Values */
+#define PWM_EN_CH(x) (1 << x) // channels 0-2
+#define RESET_VAL_PWMCS 0x00
+#define RESET_VAL_PWMIC0 0xee
+#define RESET_VAL_PWMIC1 0xb0
+#define RESET_VAL_PWMIC2 0xa3
+
+/* TODO: add PWM instruction mnemonics */
+
+/* Keypad Column Scan Register */
+#define KPC_col (BASE + 0x60)
+/* KPC_col Write Values */
+#define COLUMN_ALL (0xff << 0)
+#define COLUMN(x) (1 << x)
+#define RESET_VAL_KPC_col 0x00
+/* Keypad Row Scan Registers (MSB) */
+#define KPC_row_msb (BASE + 0x61)
+/* KPC_row_msb Write Values */
+#define KPC_SCAN_PW (u8)(0x3 << 6) // this should _always_ be set
to this value
+#define ROW_MSB(x) (0x1 << x)
+#define ROW_MSB_ALL (0xf << 0)
+#define RESET_VAL_KPC_row_msb 0xc0
+/* Keypad Row Scan Registers (LSB) */
+#define KPC_row_lsb (BASE + 0x62)
+#define ROW_LSB(x) (0x1 << x)
+#define ROW_LSB_ALL (0xff << 0)
+#define RESET_VAL_KPC_row_lsb 0x00
+
+/* Keypad Controller Register (MSB) */
+#define KPC_ctrl_msb (BASE + 0x63)
+/* KPC_ctrl_msb Write values */
+#define KPC_SCAN_CYCLES(x) (u8)(x << 4) // 0-15 cycles
+#define KPC_DEDICATED(x) (1 << x) // up to 4 dedicated keys
+#define RESET_VAL_KPC_ctrl_msb 0x00
+/* Keypad Controller Register (LSB) */
+#define KPC_ctrl_lsb (BASE + 0x64)
+/* KPC_ctrl_lsb Write values */
+#define KPC_DEBOUNCE_MS(x) (x << 1) // 0-128 ms
+#define KPC_SCAN_ENABLE (1 << 0)
+#define RESET_VAL_KPC_ctrl_lsb 0x00
+
+/* KPC_data Register */
+#define KPC_data_byte0 (BASE + 0x68)
+#define KPC_data_byte1 (BASE + 0x69)
+#define KPC_data_byte2 (BASE + 0x6A)
+#define KPCD (BASE + 0x68)
+#define KPC_data_byte(x) (KPCD + x) // 0-2 data bytes
+
+#define RESET_VAL_KPC_data_byte0 0xf8
+#define RESET_VAL_KPC_data_byte1 0xf8
+#define RESET_VAL_KPC_data_byte2 0x0f
+
+
+#define RESET_VAL(x) RESET_VAL_##x
+
+/*--------------------------------------------------------------------------*/
+
+/*
+ * The stmpe2401 uses alternate functions for its 24 GPIO lines for 3 PWM
+ * output channels, the rotator input, and the (max 12 row x 8 col) keypad
+ * controller. Although it is still possible to have all of the components
+ * running and generating interrupts simultaneously, they obviously
+ * cannot the same GPIO lines.
+ *
+ * The stmpe2401 code uses bitmaps to encode alternate functionality for
+ * each of the GPIO lines. The LSB represents GPIO line 0, while bit 23
+ * represents GPIO line 23 on (little endian machines). Therefore,
+ * to specify an 4-row by 3-column keypad, one would use
+ *
+ * u32 bmp = row_to_gio(0xf) | col_to_gio(0x7);
+ *
+ * To retreive the row and column maps for a given GPIO bitmap, bmp, use
+ *
+ * u16 row = gio_to_row( bmp );
+ * u8 col = gio_to_col( bmp );
+ *
+ * The PWM and rotator bitmaps work accordingly.
+ */
+
+#define BMP_MASK_KPD 0x1f7fff
+#define BMP_MASK_PWM 0xe00000
+#define BMP_MASK_ROT 0x1c0000
+#define BMP_MASK_GIO 0xffffff
+
+#define row_to_gio_bmp(r) ((u32)((r & 0x7f)<<8)|((r & 0xf80)<<9))
+#define col_to_gio_bmp(c) ((u32)(c & 0xff))
+#define gio_to_row_bmp(g) ((u16)(((g>>9) & 0xf80)|((g>>8) & 0x7f)))
+#define gio_to_col_bmp(g) ((u8)(g & 0xff))
+#define pwm_to_gio_bmp(p) ((u32)((p & 0x7)<<21))
+#define gio_to_pwm_bmp(g) ((u8)((g>>21) & 0x7))
+#define rot_to_gio_bmp(r) ((u32)((r & 0x7)<<18))
+#define gio_to_rot_bmp(g) ((u8)((g>>18) & 0x7))
+
+/*--------------------------------------------------------------------------*/
+#define STMPE2401_KEYMAP_SCANCODESIZE 2
+
+/*
+ * See input.h
+ */
+struct stmpe2401_keymap {
+ unsigned int keycodemax;
+ unsigned int *keycode;
+};
+/*
+ * See input.h
+ */
+struct stmpe2401_keymap_callback {
+ void (*custom)(struct input_dev *dev, int scancode );
+ int (*setkeycode)(struct input_dev *dev, int scancode, int keycode);
+};
+/*
+ * @bmp - a bitmap of GPIO to use for row outputs or column inputs
+ * @debounce - the debounce time, in milliseconds, for each key-press
+ * @callback - see the above definition
+ * @keymap - see the above definition
+ *
+ * If @callback->custom is not defined, then @keymap->keycode is expected to
+ * be a dense array, with ordered entries from 0 up to @keymap->keycodemax.
+ * The entries in @keymap->keycode are all of the scancodes, extended to
+ * 32 bits. the dense matrix must have a 1-to-1 correspondence with the
+ * integer keycodes defined in input.h. The integer keycodes in input.h can
+ * be considered as the index of scancodes in @keymap->keycode The
+ * @callback->setkeycode method can optionally be defined for a customized
+ * key-remapping function, which is particularly useful if certain keys
+ * cannot be remapped.
+ *
+ * If @callback->custom is defined, then @callback->setkeycode must also
+ * be defined. Then @keymap->keycode is expected to be a sparse array, with
+ * exactly @keymap->keycodemax entries. In contrast to the @keymap method, the
+ * entries in the sparse array do not contain scancodes, but contain all of
+ * the possible keycodes used, out of those defined in input.h. The
+ * @callback->custom method is particularly useful when key-presses trigger
+ * more than one input event.
+ *
+ * Scancodes for the STMPE2401 keypad will always be 16 bits in length.
+ * The MSB represents dedicated keys (maskable by 0xf) and the LSB represents
+ * row (0x78) / column (0x7) data. The high bit (0x80) in each byte indicates
+ * if the key is up or down. Either the low byte or the high byte will be set
+ * in the @scancode parameter, but not both. Furthermore, only one dedicated
+ * key will be reported at once.
+ *
+ * The platform-specific @callback->custom function is expected to
+ * maintain its own state information. Therefore, if more than one stmpe2401
+ * device exists on the same host (unlikely), ensure that separate entry
+ * points for @callback->custom exist in order to distinguish context
+ * information.
+ *
+ */
+struct stmpe2401_kpd_data {
+ u32 bmp, ded_keys, start_irq, end_irq;
+ u8 debounce;
+ struct stmpe2401_keymap_callback callback;
+ struct stmpe2401_keymap keymap;
+};
+
+/*--------------------------------------------------------------------------*/
+
+struct stmpe2401_pwm_data {
+ u32 bmp, start_irq, end_irq;
+};
+
+/*--------------------------------------------------------------------------*/
+
+struct stmpe2401_rot_data {
+ u32 bmp, start_irq, end_irq;
+};
+
+/*--------------------------------------------------------------------------*/
+
+struct stmpe2401_gio_data {
+ u32 bmp, start_irq, end_irq;
+};
+
+/*--------------------------------------------------------------------------*/
+
+struct stmpe2401_platform_data {
+ int nc;
+ struct stmpe2401_kpd_data *kpd;
+ struct stmpe2401_gio_data *gio;
+ struct stmpe2401_pwm_data *pwm;
+ struct stmpe2401_rot_data *rot;
+ int (*getpower)(void);
+ void (*setpower)( int on );
+ void (*reset)( void );
+};
+
+
+/*--------------------------------------------------------------------------*/
+
+// delete this stuff once the
+
+struct name_reg_map {
+ char * name;
+ u8 reg;
+};
+
+
+#define NAME_REG_FORMAT(x,y) { .name = x, .reg = y }
+
+static const struct name_reg_map regmap[] = {
+ NAME_REG_FORMAT("CHIP_ID", 0x80),
+ NAME_REG_FORMAT("VERSION_ID", 0x81),
+ NAME_REG_FORMAT("SYSCON", 0x02),
+ NAME_REG_FORMAT("ICR_msb", 0x10),
+ NAME_REG_FORMAT("ICR_lsb", 0x11),
+ NAME_REG_FORMAT("IER_msb", 0x12),
+ NAME_REG_FORMAT("IER_lsb", 0x13),
+ NAME_REG_FORMAT("ISR_msb", 0x14),
+ NAME_REG_FORMAT("ISR_lsb", 0x15),
+ NAME_REG_FORMAT("IEGPIOR_msb", 0x16),
+ NAME_REG_FORMAT("IEGPIOR_csb", 0x17),
+ NAME_REG_FORMAT("IEGPIOR_lsb", 0x18),
+ NAME_REG_FORMAT("ISGPIOR_msb", 0x19),
+ NAME_REG_FORMAT("ISGPIOR_csb", 0x1A),
+ NAME_REG_FORMAT("ISGPIOR_lsb", 0x1B),
+ NAME_REG_FORMAT("GPMR_msb", 0xA2),
+ NAME_REG_FORMAT("GPMR_csb", 0xA3),
+ NAME_REG_FORMAT("GPMR_lsb", 0xA4),
+ NAME_REG_FORMAT("GPSR_msb", 0x83),
+ NAME_REG_FORMAT("GPSR_csb", 0x84),
+ NAME_REG_FORMAT("GPSR_lsb", 0x85),
+ NAME_REG_FORMAT("GPCR_msb", 0x86),
+ NAME_REG_FORMAT("GPCR_csb", 0x87),
+ NAME_REG_FORMAT("GPCR_lsb", 0x88),
+ NAME_REG_FORMAT("GPDR_msb", 0x89),
+ NAME_REG_FORMAT("GPDR_csb", 0x8A),
+ NAME_REG_FORMAT("GPDR_lsb", 0x8B),
+ NAME_REG_FORMAT("GPEDR_msb", 0x8C),
+ NAME_REG_FORMAT("GPEDR_csb", 0x8D),
+ NAME_REG_FORMAT("GPEDR_lsb", 0x8E),
+ NAME_REG_FORMAT("GPRER_msb", 0x8F),
+ NAME_REG_FORMAT("GPRER_csb", 0x90),
+ NAME_REG_FORMAT("GPRER_lsb", 0x91),
+ NAME_REG_FORMAT("GPFER_msb", 0x92),
+ NAME_REG_FORMAT("GPFER_csb", 0x93),
+ NAME_REG_FORMAT("GPFER_lsb", 0x94),
+ NAME_REG_FORMAT("GPPUR_msb", 0x95),
+ NAME_REG_FORMAT("GPPUR_csb", 0x96),
+ NAME_REG_FORMAT("GPPUR_lsb", 0x97),
+ NAME_REG_FORMAT("GPPDR_msb", 0x98),
+ NAME_REG_FORMAT("GPPDR_csb", 0x99),
+ NAME_REG_FORMAT("GPPDR_lsb", 0x9A),
+ NAME_REG_FORMAT("GPAFR_U_msb", 0x9B),
+ NAME_REG_FORMAT("GPAFR_U_csb", 0x9C),
+ NAME_REG_FORMAT("GPAFR_U_lsb", 0x9D),
+ NAME_REG_FORMAT("GPAFR_L_msb", 0x9E),
+ NAME_REG_FORMAT("GPAFR_L_csb", 0x9F),
+ NAME_REG_FORMAT("GPAFR_L_lsb", 0xA0),
+ NAME_REG_FORMAT("PWMCS", 0x30),
+ NAME_REG_FORMAT("PWMIC0", 0x38),
+ NAME_REG_FORMAT("PWMIC1", 0x39),
+ NAME_REG_FORMAT("PWMIC2", 0x3A),
+ NAME_REG_FORMAT("KPC_col", 0x60),
+ NAME_REG_FORMAT("KPC_row_msb", 0x61),
+ NAME_REG_FORMAT("KPC_row_lsb", 0x62),
+ NAME_REG_FORMAT("KPC_ctrl_msb", 0x63),
+ NAME_REG_FORMAT("KPC_ctrl_lsb", 0x64),
+ NAME_REG_FORMAT("KPC_data_byte0", 0x68),
+ NAME_REG_FORMAT("KPC_data_byte1", 0x69),
+ NAME_REG_FORMAT("KPC_data_byte2", 0x6A),
+};
+#define SZ_STMPE2401_REGMAP 60