* keypad input method question
@ 2009-09-25 13:01 Christopher Friedt
2009-09-28 17:02 ` Dmitry Torokhov
0 siblings, 1 reply; 5+ messages in thread
From: Christopher Friedt @ 2009-09-25 13:01 UTC (permalink / raw)
To: linux-input
Hi folks,
Recently, I ported Linux to a previously unsupported device (it uses
pxa270 processer, so most of the work was already done), and I had a
couple of issues that I hope you could help me with.
1) I wrote a new 'multi-function' driver for the STMPE2401 gpio
expander, which also has a keypad controller, 3 pwm channels, and a
rotator input, and I would appreciate it if a couple of people could
review it for me so that it could be submitted upstream. So far, only
the keypad function is implemented because my device doesn't use the
others. However, I've already coded in most of the structure for the
other device functions but they just return -ENOSYS on probe. I'm sure
that someone with sufficient experience could probably look at my
driver and give me a few tips on how to improve it before submission.
Which leads me to my next question...
2) currently the input is a bit complicated - there are a couple of
different modifier keys, and I'm not really sure where to start to
have userspace handle the scancode-keycode mapping. Right now it's
done in kernel mode in a small callback function. Can somebody point
me in the right direction? It would be nice if the keypad worked at
the console and also in X or other graphical environments. Currently
I'm trying to get it to work in Android, which just uses the typical
event interface for key-remapping.
I suppose one way to do it would be to have the kernel dynamically
switch based on if the event device is open... still it's a bit
confusing. Any tips?
Regards,
Chris
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: keypad input method question
2009-09-25 13:01 keypad input method question Christopher Friedt
@ 2009-09-28 17:02 ` Dmitry Torokhov
2009-09-28 19:42 ` Christopher Friedt
0 siblings, 1 reply; 5+ messages in thread
From: Dmitry Torokhov @ 2009-09-28 17:02 UTC (permalink / raw)
To: Christopher Friedt; +Cc: linux-input
Hi Christopher,
On Fri, Sep 25, 2009 at 03:01:37PM +0200, Christopher Friedt wrote:
> Hi folks,
>
> Recently, I ported Linux to a previously unsupported device (it uses
> pxa270 processer, so most of the work was already done), and I had a
> couple of issues that I hope you could help me with.
>
> 1) I wrote a new 'multi-function' driver for the STMPE2401 gpio
> expander, which also has a keypad controller, 3 pwm channels, and a
> rotator input, and I would appreciate it if a couple of people could
> review it for me so that it could be submitted upstream. So far, only
> the keypad function is implemented because my device doesn't use the
> others. However, I've already coded in most of the structure for the
> other device functions but they just return -ENOSYS on probe. I'm sure
> that someone with sufficient experience could probably look at my
> driver and give me a few tips on how to improve it before submission.
> Which leads me to my next question...
Just post in on the mailing list (and mark that it is for initial
review).
>
> 2) currently the input is a bit complicated - there are a couple of
> different modifier keys, and I'm not really sure where to start to
> have userspace handle the scancode-keycode mapping. Right now it's
> done in kernel mode in a small callback function. Can somebody point
> me in the right direction? It would be nice if the keypad worked at
> the console and also in X or other graphical environments. Currently
> I'm trying to get it to work in Android, which just uses the typical
> event interface for key-remapping.
>
> I suppose one way to do it would be to have the kernel dynamically
> switch based on if the event device is open... still it's a bit
> confusing. Any tips?
>
I would not recommend doing it, nothing stops userspace from accessing
the device through both inetrfaces (evdev and legacy one) at the same
time.
It would be curious to see what kind of keypad you have.
--
Dmitry
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: keypad input method question
2009-09-28 17:02 ` Dmitry Torokhov
@ 2009-09-28 19:42 ` Christopher Friedt
2009-09-29 9:30 ` Kristoffer Ericson
0 siblings, 1 reply; 5+ messages in thread
From: Christopher Friedt @ 2009-09-28 19:42 UTC (permalink / raw)
To: linux-input
Hi Dimitry,
Thanks for the reply!
On Mon, Sep 28, 2009 at 7:02 PM, Dmitry Torokhov
<dmitry.torokhov@gmail.com> wrote:
> Just post in on the mailing list (and mark that it is for initial
> review).
My patch is here [1]. There are some obvious sections to remove - for
one, declaring a massive static array of string <-> register
combinations that I was using to debug before I discovered i2c-dev /
i2c-utils. I also need to properly unregister the input device when
rmmod is called, and cancel any pending work. It was giving me a
kernel panic when I tried that before, so I just commented that part
out (very bad, i know), so every time the module is inserted it
creates a new input device without entirely removing the previous one.
Also, because I'm using bitmaps for the actual pin configuration, it
might make it tricky for big-endian systems.
Regarding the keypad complexity ...
> ... nothing stops userspace from accessing
> the device through both inetrfaces (evdev and legacy one) at the same
> time.
>
> It would be curious to see what kind of keypad you have.
There is an image of the keypad here [2]. The alpha key cycles through
3 states: 0=numbers, 1=lowercase, 2=uppercase. The keys also respond
to multi-tap, so hitting the '2' key twice would result in 'b' when
alpha=1. The way I implemented multi-tap was to use a state machine &
timer, and send a backspace followed by the next logical key, if the
timer had not expired first. Aside from the alpha key, there's also
the 'fn' modifier key, which would generate e.g. pageup when fn, then
up, were pressed. I currently also don't support holding buttons down,
simply because it's a bit clumsy with small keypads like this. All of
the modifier keys work on a sequential basis, not a combinatorial
basis.
This keypad driver can obviously be done in userspace just as well as
kernel space, but would require a special driver in that case too...
unless you can suggest some magical keycode-scanning method. I did
think about using alpha-states to encode numlock & capslock, so in
that case there might only be one actual modifier key. Also, it's
possible to have a userspace driver change the keycode-mapping for
multi-tap and issue backspaces as appropriate... so that is one
possibility for a keycode-scanning method, but again, I'm not really
sure where to start with that for X ... or Android (yet). It works
fine in X and on the console right now, but I think I'll need to
implement a userspace driver for Android because the lack of a
keycode-scanning feature makes webkit crash.
Of course, it's only _my_ keypad that is so complicated - the
complexity of the keypad is entirely independent from the stmpe2401
driver. The device is capable of reporting 2 simultaneous button
presses, or 3 if dedicated keys are also used, but I'm currently only
reporting one at a time with the initial driver, just to make life
easier.
Please feel free to give me as much constructive criticism as you wish :)
C
[1] http://virta.visibleassets.com/~cfriedt/cl/kernel/stmpe2401-20090923.patch
[2] http://virta.visibleassets.com/~cfriedt/cl/keypad-28.jpg
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: keypad input method question
2009-09-28 19:42 ` Christopher Friedt
@ 2009-09-29 9:30 ` Kristoffer Ericson
2009-09-29 18:43 ` Christopher Friedt
0 siblings, 1 reply; 5+ messages in thread
From: Kristoffer Ericson @ 2009-09-29 9:30 UTC (permalink / raw)
To: Christopher Friedt; +Cc: linux-input
On Mon, 28 Sep 2009 21:42:55 +0200
Christopher Friedt <chrisfriedt@gmail.com> wrote:
> Hi Dimitry,
>
> Thanks for the reply!
Please resend with patch inside email (pasted) since its alot easier
to comment on code that way.
>
> On Mon, Sep 28, 2009 at 7:02 PM, Dmitry Torokhov
> <dmitry.torokhov@gmail.com> wrote:
> > Just post in on the mailing list (and mark that it is for initial
> > review).
>
> My patch is here [1]. There are some obvious sections to remove - for
> one, declaring a massive static array of string <-> register
> combinations that I was using to debug before I discovered i2c-dev /
> i2c-utils. I also need to properly unregister the input device when
> rmmod is called, and cancel any pending work. It was giving me a
> kernel panic when I tried that before, so I just commented that part
> out (very bad, i know), so every time the module is inserted it
> creates a new input device without entirely removing the previous one.
> Also, because I'm using bitmaps for the actual pin configuration, it
> might make it tricky for big-endian systems.
>
> Regarding the keypad complexity ...
>
> > ... nothing stops userspace from accessing
> > the device through both inetrfaces (evdev and legacy one) at the same
> > time.
> >
> > It would be curious to see what kind of keypad you have.
>
> There is an image of the keypad here [2]. The alpha key cycles through
> 3 states: 0=numbers, 1=lowercase, 2=uppercase. The keys also respond
> to multi-tap, so hitting the '2' key twice would result in 'b' when
> alpha=1. The way I implemented multi-tap was to use a state machine &
> timer, and send a backspace followed by the next logical key, if the
> timer had not expired first. Aside from the alpha key, there's also
> the 'fn' modifier key, which would generate e.g. pageup when fn, then
> up, were pressed. I currently also don't support holding buttons down,
> simply because it's a bit clumsy with small keypads like this. All of
> the modifier keys work on a sequential basis, not a combinatorial
> basis.
>
> This keypad driver can obviously be done in userspace just as well as
> kernel space, but would require a special driver in that case too...
> unless you can suggest some magical keycode-scanning method. I did
> think about using alpha-states to encode numlock & capslock, so in
> that case there might only be one actual modifier key. Also, it's
> possible to have a userspace driver change the keycode-mapping for
> multi-tap and issue backspaces as appropriate... so that is one
> possibility for a keycode-scanning method, but again, I'm not really
> sure where to start with that for X ... or Android (yet). It works
> fine in X and on the console right now, but I think I'll need to
> implement a userspace driver for Android because the lack of a
> keycode-scanning feature makes webkit crash.
>
> Of course, it's only _my_ keypad that is so complicated - the
> complexity of the keypad is entirely independent from the stmpe2401
> driver. The device is capable of reporting 2 simultaneous button
> presses, or 3 if dedicated keys are also used, but I'm currently only
> reporting one at a time with the initial driver, just to make life
> easier.
>
> Please feel free to give me as much constructive criticism as you wish :)
>
> C
>
> [1] http://virta.visibleassets.com/~cfriedt/cl/kernel/stmpe2401-20090923.patch
> [2] http://virta.visibleassets.com/~cfriedt/cl/keypad-28.jpg
> --
> To unsubscribe from this list: send the line "unsubscribe linux-input" in
> the body of a message to majordomo@vger.kernel.org
> More majordomo info at http://vger.kernel.org/majordomo-info.html
--
Kristoffer Ericson <kristoffer.ericson@gmail.com>
^ permalink raw reply [flat|nested] 5+ messages in thread
* Re: keypad input method question
2009-09-29 9:30 ` Kristoffer Ericson
@ 2009-09-29 18:43 ` Christopher Friedt
0 siblings, 0 replies; 5+ messages in thread
From: Christopher Friedt @ 2009-09-29 18:43 UTC (permalink / raw)
To: linux-input
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a6b989a..e8b9f25 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -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
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index b5b5eae..9f67adc 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -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
--- /dev/null
+++ b/drivers/input/keyboard/stmpe2401.c
@@ -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
+ *
+ */
diff --git a/include/linux/i2c/stmpe2401.h b/include/linux/i2c/stmpe2401.h
new file mode 100644
index 0000000..e8deec3
--- /dev/null
+++ b/include/linux/i2c/stmpe2401.h
@@ -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
+#endif /* __LINUX_I2C_STMPE2401_H */
^ permalink raw reply related [flat|nested] 5+ messages in thread
end of thread, other threads:[~2009-09-29 18:43 UTC | newest]
Thread overview: 5+ messages (download: mbox.gz follow: Atom feed
-- links below jump to the message on this page --
2009-09-25 13:01 keypad input method question Christopher Friedt
2009-09-28 17:02 ` Dmitry Torokhov
2009-09-28 19:42 ` Christopher Friedt
2009-09-29 9:30 ` Kristoffer Ericson
2009-09-29 18:43 ` Christopher Friedt
This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for NNTP newsgroup(s).