summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorRaj Rajasekaran <b10872@freescale.com>2008-04-11 13:56:05 -0500
committerDaniel Schaeffer <daniel.schaeffer@timesys.com>2008-08-25 15:21:01 -0400
commit1120ae3bbf7edfa2d929b98d6d7ab5653b97fad1 (patch)
tree40808cf7433e19de15b4c707fc5d33e8325e9571
parent2ac500ab6c3b7d00e7be61d8a6a70b4133840e6e (diff)
ENGR00072542: Add support for MPR084 Touch Keypad driver.
Added keypad driver for MX37 3 stack board. Signed-off-by: Raj Rajasekaran <b10872@freescale.com>
-rw-r--r--arch/arm/configs/imx37_3stack_defconfig3
-rw-r--r--arch/arm/mach-mx37/mx37_3stack.c28
-rw-r--r--arch/arm/mach-mx37/mx37_3stack_gpio.c44
-rw-r--r--drivers/i2c/busses/mxc_i2c.c18
-rw-r--r--drivers/input/keyboard/Kconfig7
-rw-r--r--drivers/input/keyboard/Makefile1
-rw-r--r--drivers/input/keyboard/mpr084.c384
-rw-r--r--include/asm-arm/arch-mxc/mxc.h6
8 files changed, 486 insertions, 5 deletions
diff --git a/arch/arm/configs/imx37_3stack_defconfig b/arch/arm/configs/imx37_3stack_defconfig
index 065143416168..9404c158c1e8 100644
--- a/arch/arm/configs/imx37_3stack_defconfig
+++ b/arch/arm/configs/imx37_3stack_defconfig
@@ -632,7 +632,8 @@ CONFIG_INPUT_EVDEV=y
#
# Input Device Drivers
#
-# CONFIG_INPUT_KEYBOARD is not set
+CONFIG_INPUT_KEYBOARD=y
+CONFIG_KEYBOARD_MPR084=y
# CONFIG_INPUT_MOUSE is not set
# CONFIG_INPUT_JOYSTICK is not set
# CONFIG_INPUT_TABLET is not set
diff --git a/arch/arm/mach-mx37/mx37_3stack.c b/arch/arm/mach-mx37/mx37_3stack.c
index 8ea8d38c9eb3..cc227a7829ad 100644
--- a/arch/arm/mach-mx37/mx37_3stack.c
+++ b/arch/arm/mach-mx37/mx37_3stack.c
@@ -38,7 +38,6 @@
#include <asm/setup.h>
#include <asm/mach-types.h>
#include <asm/mach/arch.h>
-#include <asm/mach/keypad.h>
#include <asm/arch/memory.h>
#include <asm/arch/gpio.h>
#include <asm/arch/mmc.h>
@@ -159,12 +158,39 @@ static struct mxc_lcd_platform_data lcd_data = {
.reset = lcd_reset,
};
+#if defined(CONFIG_KEYBOARD_MPR084) || defined(CONFIG_KEYBOARD_MPR084_MODULE)
+/*!
+ * These functions are used to configure and the GPIO pins for keypad to
+ * activate and deactivate it.
+ */
+extern void gpio_keypad_active(void);
+
+extern void gpio_keypad_inactive(void);
+
+static u16 keymap[] = {
+ KEY_DOWN, KEY_LEFT, KEY_ENTER,
+ KEY_RIGHT, KEY_UP, KEY_LEFTALT,
+ KEY_TAB, KEY_ESC,
+};
+
+static struct mxc_keyp_platform_data keypad_data = {
+ .matrix = keymap,
+ .active = gpio_keypad_active,
+ .inactive = gpio_keypad_inactive,
+};
+#endif
static struct i2c_board_info mxc_i2c0_board_info[] __initdata = {
{
.driver_name = "TSC2007",
.addr = 0x48,
.irq = IOMUX_TO_IRQ(MX37_PIN_AUD5_RXFS),
},
+ {
+ .driver_name = "MPR084",
+ .addr = 0x5D,
+ .platform_data = &keypad_data,
+ .irq = IOMUX_TO_IRQ(MX37_PIN_GPIO1_3),
+ },
};
static struct spi_board_info mxc_spi_board_info[] __initdata = {
diff --git a/arch/arm/mach-mx37/mx37_3stack_gpio.c b/arch/arm/mach-mx37/mx37_3stack_gpio.c
index 9a6d56fcece1..ddb0cc59448d 100644
--- a/arch/arm/mach-mx37/mx37_3stack_gpio.c
+++ b/arch/arm/mach-mx37/mx37_3stack_gpio.c
@@ -23,7 +23,7 @@
#include "iomux.h"
/*!
- * @file mx31ads_gpio.c
+ * @file mx37_3stack_gpio.c
*
* @brief This file contains all the GPIO setup functions for the board.
*
@@ -598,6 +598,48 @@ void gpio_ata_inactive(void)
EXPORT_SYMBOL(gpio_ata_inactive);
+/*!
+ * Setup GPIO for Keypad to be active
+ *
+ */
+void gpio_keypad_active(void)
+{
+ int pad_val;
+
+ /*
+ * Configure the IOMUX control register for keypad signals.
+ */
+ /*KEY_INT */
+ mxc_request_iomux(MX37_PIN_GPIO1_3, IOMUX_CONFIG_ALT0);
+ /*KEY_WAKE */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT4);
+
+ /* fast slew rate */
+ pad_val = (PAD_CTL_SRE_FAST | PAD_CTL_PKE_NONE | PAD_CTL_100K_PU);
+ /*KEY_INT */
+ mxc_iomux_set_pad(MX37_PIN_GPIO1_3, pad_val);
+ /*KEY_WAKE */
+ mxc_iomux_set_pad(MX37_PIN_DISP1_DAT18, pad_val);
+
+ mxc_set_gpio_direction(MX37_PIN_DISP1_DAT18, 0);
+ mxc_set_gpio_direction(MX37_PIN_GPIO1_3, 1);
+
+}
+EXPORT_SYMBOL(gpio_keypad_active);
+
+/*!
+ * Setup GPIO for Keypad to be inactive
+ *
+ */
+void gpio_keypad_inactive(void)
+{
+ /*KEY_INT */
+ mxc_request_iomux(MX37_PIN_GPIO1_3, IOMUX_CONFIG_ALT0);
+ /*KEY_WAKE */
+ mxc_request_iomux(MX37_PIN_DISP1_DAT18, IOMUX_CONFIG_ALT0);
+}
+EXPORT_SYMBOL(gpio_keypad_inactive);
+
/*
* USB OTG HS port
*/
diff --git a/drivers/i2c/busses/mxc_i2c.c b/drivers/i2c/busses/mxc_i2c.c
index 5d94f6ce37b2..3b66026ec2d6 100644
--- a/drivers/i2c/busses/mxc_i2c.c
+++ b/drivers/i2c/busses/mxc_i2c.c
@@ -1,5 +1,5 @@
/*
- * Copyright 2004-2007 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2004-2008 Freescale Semiconductor, Inc. All Rights Reserved.
*/
/*
@@ -122,11 +122,25 @@ extern void gpio_i2c_inactive(int i2c_num);
*/
static void mxc_i2c_stop(mxc_i2c_device * dev)
{
- volatile unsigned int cr;
+ unsigned int cr, sr;
+ int retry = 16;
+ spinlock_t lock;
+ spin_lock(&lock);
cr = readw(dev->membase + MXC_I2CR);
cr &= ~(MXC_I2CR_MSTA | MXC_I2CR_MTX);
writew(cr, dev->membase + MXC_I2CR);
+
+ /* Wait till the Bus Busy bit is reset */
+ sr = readw(dev->membase + MXC_I2SR);
+ while (retry-- && ((sr & MXC_I2SR_IBB))) {
+ udelay(3);
+ sr = readw(dev->membase + MXC_I2SR);
+ }
+ spin_unlock(&lock);
+ if (retry <= 0)
+ printk(KERN_DEBUG "Could not set I2C Bus Busy bit to zero.\n");
+
}
/*!
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 290254d23a29..8bb766891a8e 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -300,4 +300,11 @@ config KEYBOARD_BFIN
To compile this driver as a module, choose M here: the
module will be called bf54x-keys.
+config KEYBOARD_MPR084
+ tristate "Freescale MPR084 Touch Keypad Driver"
+ depends on ARCH_MX37
+ help
+ This is the Keypad driver for the Freescale Proximity Capacitive
+ Touch Sensor controller chip.
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index b2b9abe5e645..88e25749f0a2 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_KEYBOARD_HP6XX) += jornada680_kbd.o
obj-$(CONFIG_KEYBOARD_HP7XX) += jornada720_kbd.o
obj-$(CONFIG_KEYBOARD_MAPLE) += maple_keyb.o
obj-$(CONFIG_KEYBOARD_BFIN) += bf54x-keys.o
+obj-$(CONFIG_KEYBOARD_MPR084) +=mpr084.o \ No newline at end of file
diff --git a/drivers/input/keyboard/mpr084.c b/drivers/input/keyboard/mpr084.c
new file mode 100644
index 000000000000..f1109ab5cf6d
--- /dev/null
+++ b/drivers/input/keyboard/mpr084.c
@@ -0,0 +1,384 @@
+/*
+ * Copyright 2008 Freescale Semiconductor, Inc. All Rights Reserved.
+ */
+
+/*
+ * The code contained herein is licensed under the GNU General Public
+ * License. You may obtain a copy of the GNU General Public License
+ * Version 2 or later at the following locations:
+ *
+ * http://www.opensource.org/licenses/gpl-license.html
+ * http://www.gnu.org/copyleft/gpl.html
+ */
+
+/*!
+ * @file linux/drivers/input/keyboard/mpr084.c
+ *
+ * @brief Driver for the Freescale MPR084 I2C Touch Sensor KeyPad module.
+ *
+ *
+ *
+ * @ingroup Keypad
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/i2c.h>
+#include <linux/string.h>
+#include <linux/bcd.h>
+#include <linux/list.h>
+#include <linux/device.h>
+#include <linux/interrupt.h>
+#include <linux/kthread.h>
+#include <linux/input.h>
+#include <linux/delay.h>
+#include <asm/mach/irq.h>
+#include <asm/arch/gpio.h>
+
+/*
+ * Definitions
+ */
+#define DEBUG 0
+
+#define KEY_COUNT 8
+
+/*
+ *Registers in MPR084
+ */
+#define MPR084_FIFO_ADDR 0x00
+#define MPR084_FAULT_ADDR 0x01
+#define MPR084_TPS_ADDR 0x02
+#define MPR084_TPC_ADDR 0x03
+#define MPR084_STR1_ADDR 0x04
+#define MPR084_STR2_ADDR 0x05
+#define MPR084_STR3_ADDR 0x06
+#define MPR084_STR4_ADDR 0x07
+#define MPR084_STR5_ADDR 0x08
+#define MPR084_STR6_ADDR 0x09
+#define MPR084_STR7_ADDR 0x0A
+#define MPR084_STR8_ADDR 0x0B
+#define MPR084_ECEM_ADDR 0x0C
+#define MPR084_MNTP_ADDR 0x0D
+#define MPR084_MTC_ADDR 0x0E
+#define MPR084_TASP_ADDR 0x0F
+#define MPR084_SC_ADDR 0x10
+#define MPR084_LPC_ADDR 0x11
+#define MPR084_SKT_ADDR 0x12
+#define MPR084_CONFIG_ADDR 0x13
+#define MPR084_SI_ADDR 0x14
+#define MPR084_ADDR_MINI MPR084_FIFO_ADDR
+#define MPR084_ADDR_MAX MPR084_SI_ADDR
+
+/* FIFO registers */
+#define MPR084_FIFO_MORE_DATA_FLAG 0x80
+#define MPR084_FIFO_NO_DATA_FLAG 0x40
+#define MPR084_FIFO_OVERFLOW_FLAG 0x20
+#define MPR084_FIFO_PAD_IS_TOUCHED 0x10
+#define MPR084_FIFO_POSITION_MASK 0x0F
+
+#define DRIVER_NAME "MPR084"
+
+struct mpr084_data {
+ struct i2c_client *client;
+ struct device_driver driver;
+ struct input_dev *idev;
+ struct task_struct *tstask;
+ struct completion kpirq_completion;
+ int kpirq;
+ int kp_thread_cnt;
+};
+
+static int kpstatus[KEY_COUNT];
+static struct mxc_keyp_platform_data *keypad;
+static const unsigned short *mxckpd_keycodes;
+
+static int mpr084_read_register(struct mpr084_data *data,
+ unsigned char regaddr, int *value)
+{
+ int ret = 0;
+ unsigned char regvalue;
+
+ ret = i2c_master_send(data->client, &regaddr, 1);
+ if (ret < 0)
+ goto err;
+ udelay(20);
+ ret = i2c_master_recv(data->client, &regvalue, 1);
+ if (ret < 0)
+ goto err;
+ *value = regvalue;
+
+ return ret;
+err:
+ return -ENODEV;
+}
+
+static irqreturn_t mpr084_keypadirq(int irq, void *v)
+{
+ struct mpr084_data *d = v;
+
+ disable_irq(d->kpirq);
+ complete(&d->kpirq_completion);
+ return IRQ_HANDLED;
+}
+
+static int mpr084ts_thread(void *v)
+{
+ struct mpr084_data *d = v;
+ int ret = 0, fifo = 0;
+ int index = 0, currentstatus = 0;
+
+ if (d->kp_thread_cnt)
+ return -EINVAL;
+ d->kp_thread_cnt = 1;
+ while (1) {
+
+ if (kthread_should_stop())
+ break;
+ /* Wait for keypad interrupt */
+ if (wait_for_completion_interruptible_timeout
+ (&d->kpirq_completion, HZ) <= 0)
+ continue;
+ ret = mpr084_read_register(d, MPR084_FIFO_ADDR, &fifo);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad FIFO register \n\n",
+ __func__);
+ } else {
+ if (fifo & MPR084_FIFO_OVERFLOW_FLAG)
+ printk(KERN_ERR
+ "%s: FIFO overflow \n\n", __func__);
+ while (!(fifo & MPR084_FIFO_NO_DATA_FLAG)) {
+ index = fifo & MPR084_FIFO_POSITION_MASK;
+ currentstatus =
+ fifo & MPR084_FIFO_PAD_IS_TOUCHED;
+ /*Scan key map for changes */
+ if ((currentstatus) ^ (kpstatus[index])) {
+ if (!(currentstatus)) {
+ /*Key released. */
+ input_event(d->idev, EV_KEY,
+ mxckpd_keycodes
+ [index], 0);
+ } else {
+ /* Key pressed. */
+ input_event(d->idev, EV_KEY,
+ mxckpd_keycodes
+ [index], 1);
+ }
+ /*Store current keypad status */
+ kpstatus[index] = currentstatus;
+ }
+ mpr084_read_register(d, MPR084_FIFO_ADDR,
+ &fifo);
+ if (fifo & MPR084_FIFO_OVERFLOW_FLAG)
+ printk(KERN_ERR
+ "%s: FIFO overflow \n\n",
+ __func__);
+ }
+ }
+ /* Re-enable interrupts */
+ enable_irq(d->kpirq);
+ }
+
+ d->kp_thread_cnt = 0;
+ return 0;
+}
+
+static int mpr084_idev_open(struct input_dev *idev)
+{
+ struct mpr084_data *d = idev->private;
+ int ret = 0;
+
+ d->tstask = kthread_run(mpr084ts_thread, d, DRIVER_NAME "kpd");
+ if (IS_ERR(d->tstask))
+ ret = PTR_ERR(d->tstask);
+ return ret;
+}
+
+static void mpr084_idev_close(struct input_dev *idev)
+{
+ struct mpr084_data *d = idev->private;
+
+ if (!IS_ERR(d->tstask))
+ kthread_stop(d->tstask);
+}
+
+static int mpr084_driver_register(struct mpr084_data *data)
+{
+ struct input_dev *idev;
+ int ret = 0;
+
+ if (data->kpirq) {
+ ret =
+ request_irq(data->kpirq, mpr084_keypadirq,
+ IRQF_TRIGGER_FALLING, DRIVER_NAME, data);
+ if (!ret) {
+ init_completion(&data->kpirq_completion);
+ } else {
+ printk(KERN_ERR "%s: cannot grab irq %d\n",
+ __func__, data->kpirq);
+ }
+
+ }
+ idev = input_allocate_device();
+ data->idev = idev;
+ idev->private = data;
+ idev->name = DRIVER_NAME;
+ idev->open = mpr084_idev_open;
+ idev->close = mpr084_idev_close;
+ if (!ret)
+ ret = input_register_device(idev);
+
+ return ret;
+}
+
+static int mpr084_i2c_remove(struct i2c_client *client)
+{
+ int err;
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ free_irq(d->kpirq, d);
+ input_unregister_device(d->idev);
+ if (keypad->inactive)
+ keypad->inactive();
+ err = i2c_detach_client(client);
+ if (err) {
+ dev_err(&client->dev, "Client deregistration failed, "
+ "client not detached.\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static int mpr084_write_register(struct mpr084_data *data,
+ u8 regaddr, u8 regvalue)
+{
+ int ret = 0;
+ unsigned char msgbuf[2];
+
+ msgbuf[0] = regaddr;
+ msgbuf[1] = regvalue;
+ ret = i2c_master_send(data->client, msgbuf, 2);
+ if (ret < 0) {
+ printk(KERN_ERR "%s - Error in writing to I2C Register %d \n",
+ __func__, regaddr);
+ return ret;
+ }
+
+ return ret;
+}
+static int mpr084_configure(struct mpr084_data *data)
+{
+ int ret = 0;
+
+ ret = mpr084_write_register(data, MPR084_TPC_ADDR, 0xbd);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR1_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR2_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR3_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR4_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR5_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR6_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR7_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR8_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+ /* channel enable mask: enable all */
+ ret = mpr084_write_register(data, MPR084_ECEM_ADDR, 0xff);
+ if (ret < 0)
+ goto err;
+ /*two conccurrent touch position allowed */
+ ret = mpr084_write_register(data, MPR084_MNTP_ADDR, 0x07);
+ if (ret < 0)
+ goto err;
+ /*Sample period */
+ ret = mpr084_write_register(data, MPR084_TASP_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ /*enabled IRQEN, RUNE, IRQR */
+ ret = mpr084_write_register(data, MPR084_CONFIG_ADDR, 0x17);
+ if (ret < 0)
+ goto err;
+ return ret;
+err:
+ return -ENODEV;
+}
+
+static int mpr084_i2c_probe(struct i2c_client *client)
+{
+ struct mpr084_data *data;
+ int err = 0, i = 0;
+
+ data = kzalloc(sizeof(struct mpr084_data), GFP_KERNEL);
+ if (data == NULL)
+ return -ENOMEM;
+ i2c_set_clientdata(client, data);
+ data->client = client;
+ data->kpirq = client->irq;
+ err = mpr084_driver_register(data);
+ if (err < 0)
+ goto exit_free;
+ keypad = (struct mxc_keyp_platform_data *)(client->dev).platform_data;
+ if (keypad->active)
+ keypad->active();
+ mxckpd_keycodes = keypad->matrix;
+ data->idev->keycode = &mxckpd_keycodes;
+ data->idev->keycodesize = sizeof(unsigned char);
+ data->idev->keycodemax = KEY_COUNT;
+ data->idev->id.bustype = BUS_I2C;
+ __set_bit(EV_KEY, data->idev->evbit);
+ for (i = 0; i < 8; i++)
+ __set_bit(mxckpd_keycodes[i], data->idev->keybit);
+ err = mpr084_configure(data);
+ if (err == -ENODEV) {
+ free_irq(data->kpirq, data);
+ input_unregister_device(data->idev);
+ goto exit_free;
+ }
+ memset(kpstatus, 0, sizeof(kpstatus));
+ printk(KERN_INFO "%s: Device Attached\n", __func__);
+ return 0;
+exit_free:
+ kfree(data);
+ return err;
+}
+
+static struct i2c_driver mpr084_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = mpr084_i2c_probe,
+ .remove = mpr084_i2c_remove,
+ .command = NULL,
+};
+static int __init mpr084_init(void)
+{
+ return i2c_add_driver(&mpr084_driver);
+}
+
+static void __exit mpr084_exit(void)
+{
+ i2c_del_driver(&mpr084_driver);
+}
+
+MODULE_AUTHOR("Freescale Semiconductor Inc");
+MODULE_DESCRIPTION("MPR084 Touch KeyPad Controller driver");
+MODULE_LICENSE("GPL");
+module_init(mpr084_init);
+module_exit(mpr084_exit);
diff --git a/include/asm-arm/arch-mxc/mxc.h b/include/asm-arm/arch-mxc/mxc.h
index d901cc92e984..50442b78f867 100644
--- a/include/asm-arm/arch-mxc/mxc.h
+++ b/include/asm-arm/arch-mxc/mxc.h
@@ -149,6 +149,12 @@ struct mxc_mma7450_platform_data {
int int2;
};
+struct mxc_keyp_platform_data {
+ u16 *matrix;
+ void (*active) (void);
+ void (*inactive) (void);
+};
+
extern void mxc_wd_reset(void);
extern void mxc_kick_wd(void);
unsigned long board_get_ckih_rate(void);