From 1120ae3bbf7edfa2d929b98d6d7ab5653b97fad1 Mon Sep 17 00:00:00 2001 From: Raj Rajasekaran Date: Fri, 11 Apr 2008 13:56:05 -0500 Subject: ENGR00072542: Add support for MPR084 Touch Keypad driver. Added keypad driver for MX37 3 stack board. Signed-off-by: Raj Rajasekaran --- arch/arm/configs/imx37_3stack_defconfig | 3 +- arch/arm/mach-mx37/mx37_3stack.c | 28 ++- arch/arm/mach-mx37/mx37_3stack_gpio.c | 44 +++- drivers/i2c/busses/mxc_i2c.c | 18 +- drivers/input/keyboard/Kconfig | 7 + drivers/input/keyboard/Makefile | 1 + drivers/input/keyboard/mpr084.c | 384 ++++++++++++++++++++++++++++++++ include/asm-arm/arch-mxc/mxc.h | 6 + 8 files changed, 486 insertions(+), 5 deletions(-) create mode 100644 drivers/input/keyboard/mpr084.c 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 #include #include -#include #include #include #include @@ -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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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, ®addr, 1); + if (ret < 0) + goto err; + udelay(20); + ret = i2c_master_recv(data->client, ®value, 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); -- cgit v1.2.3