summaryrefslogtreecommitdiff
path: root/drivers/input
diff options
context:
space:
mode:
authorJustin Waters <justin.waters@timesys.com>2012-03-21 13:28:20 -0400
committerJustin Waters <justin.waters@timesys.com>2012-03-21 13:28:20 -0400
commitd0183eb2433e3332c2720637238b18b1fdff7946 (patch)
tree36be0be2c433789656750da0ca5991250fc7d3e7 /drivers/input
parent74fca6a42863ffacaf7ba6f1936a9f228950f657 (diff)
Add support for the i.MX28 EVK
This patch was originally put together in January 2011 by Roshni.
Diffstat (limited to 'drivers/input')
-rw-r--r--drivers/input/keyboard/Kconfig34
-rw-r--r--drivers/input/keyboard/Makefile5
-rw-r--r--drivers/input/keyboard/mc9s08dz60_keyb.c248
-rw-r--r--drivers/input/keyboard/mpr084.c500
-rw-r--r--drivers/input/keyboard/mxc_keyb.c1202
-rw-r--r--drivers/input/keyboard/mxs-kbd.c364
-rw-r--r--drivers/input/keyboard/stmp3xxx-kbd.c307
-rw-r--r--drivers/input/misc/Kconfig9
-rw-r--r--drivers/input/misc/Makefile1
-rw-r--r--drivers/input/misc/stmp3xxx_rotdec.c174
-rw-r--r--drivers/input/touchscreen/Kconfig40
-rw-r--r--drivers/input/touchscreen/Makefile4
-rw-r--r--drivers/input/touchscreen/imx_adc_ts.c114
-rw-r--r--drivers/input/touchscreen/mxc_ts.c189
-rw-r--r--drivers/input/touchscreen/mxs-ts.c462
-rw-r--r--drivers/input/touchscreen/stmp3xxx_ts.c422
-rw-r--r--drivers/input/touchscreen/tsc2007.c55
17 files changed, 4095 insertions, 35 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index a6b989a9dc07..89803885cadf 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -310,6 +310,13 @@ config KEYBOARD_SUNKBD
To compile this driver as a module, choose M here: the
module will be called sunkbd.
+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.
+
config KEYBOARD_SH_KEYSC
tristate "SuperH KEYSC keypad support"
depends on SUPERH
@@ -317,6 +324,7 @@ config KEYBOARD_SH_KEYSC
Say Y here if you want to use a keypad attached to the KEYSC block
on SuperH processors such as sh7722 and sh7343.
+
To compile this driver as a module, choose M here: the
module will be called sh_keysc.
@@ -361,4 +369,30 @@ config KEYBOARD_XTKBD
To compile this driver as a module, choose M here: the
module will be called xtkbd.
+config KEYBOARD_MXC
+ tristate "MXC Keypad Driver"
+ depends on ARCH_MXC
+ help
+ This is the Keypad driver for the Freescale MXC application
+ processors.
+
+config KEYBOARD_STMP3XXX
+ tristate "STMP3xxx keyboard"
+ depends on ARCH_STMP3XXX
+ help
+ -to be written-
+
+config KEYBOARD_MXS
+ tristate "MXS keyboard"
+ depends on ARCH_MXS
+ help
+ This is the Keypad driver for the Freescale mxs soc
+
+
+config KEYBOARD_MC9S08DZ60
+ tristate "mc9s08dz60 keyboard"
+ depends on MXC_PMIC_MC9S08DZ60
+ help
+ -to be written-
+
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index b5b5eae9724f..fea01a591076 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -31,3 +31,8 @@ obj-$(CONFIG_KEYBOARD_STOWAWAY) += stowaway.o
obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TOSA) += tosakbd.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o
+obj-$(CONFIG_KEYBOARD_MPR084) += mpr084.o
+obj-$(CONFIG_KEYBOARD_STMP3XXX) += stmp3xxx-kbd.o
+obj-$(CONFIG_KEYBOARD_MXS) += mxs-kbd.o
+obj-$(CONFIG_KEYBOARD_MC9S08DZ60) += mc9s08dz60_keyb.o
diff --git a/drivers/input/keyboard/mc9s08dz60_keyb.c b/drivers/input/keyboard/mc9s08dz60_keyb.c
new file mode 100644
index 000000000000..8ec9f646c7c7
--- /dev/null
+++ b/drivers/input/keyboard/mc9s08dz60_keyb.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2009 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 mc9s08dz60keyb.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC keypad port.
+ *
+ * The keypad driver is designed as a standard Input driver which interacts
+ * with low level keypad port hardware. Upon opening, the Keypad driver
+ * initializes the keypad port. When the keypad interrupt happens the driver
+ * calles keypad polling timer and scans the keypad matrix for key
+ * press/release. If all key press/release happened it comes out of timer and
+ * waits for key press interrupt. The scancode for key press and release events
+ * are passed to Input subsytem.
+ *
+ * @ingroup keypad
+ */
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <mach/hardware.h>
+#include <linux/kd.h>
+#include <linux/fs.h>
+#include <linux/kbd_kern.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/mfd/mc9s08dz60/pmic.h>
+#include <asm/mach/keypad.h>
+
+#define MOD_NAME "mc9s08dz60-keyb"
+/*
+ * Module header file
+ */
+
+/*! Input device structure. */
+static struct input_dev *mc9s08dz60kbd_dev;
+static unsigned int key_status;
+static int keypad_irq;
+static unsigned int key_code_map[8] = {
+ KEY_LEFT,
+ KEY_DOWN,
+ 0,
+ 0,
+ KEY_UP,
+ KEY_RIGHT,
+ 0,
+ 0,
+};
+static unsigned int keycodes_size = 8;
+
+static void read_key_handler(struct work_struct *work);
+static DECLARE_WORK(key_pad_event, read_key_handler);
+
+
+static void read_key_handler(struct work_struct *work)
+{
+ unsigned int val1, val2;
+ int pre_val, curr_val, i;
+ val1 = val2 = 0xff;
+ mcu_pmic_read_reg(REG_MCU_KPD_1, &val1, 0xff);
+ mcu_pmic_read_reg(REG_MCU_KPD_2, &val2, 0xff);
+ pr_debug("key pressed, 0x%02x%02x\n", val2, val1);
+ for (i = 0; i < 8; i++) {
+ curr_val = (val1 >> i) & 0x1;
+ if (curr_val > 0)
+ input_event(mc9s08dz60kbd_dev, EV_KEY,
+ key_code_map[i], 1);
+ else {
+ pre_val = (key_status >> i) & 0x1;
+ if (pre_val > 0)
+ input_event(mc9s08dz60kbd_dev, EV_KEY,
+ key_code_map[i], 0);
+ }
+ }
+ key_status = val1;
+
+}
+
+static irqreturn_t mc9s08dz60kpp_interrupt(int irq, void *dev_id)
+{
+ schedule_work(&key_pad_event);
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function is called when the keypad driver is opened.
+ * Since keypad initialization is done in __init, nothing is done in open.
+ *
+ * @param dev Pointer to device inode
+ *
+ * @result The function always return 0
+ */
+static int mc9s08dz60kpp_open(struct input_dev *dev)
+{
+ return 0;
+}
+
+/*!
+ * This function is called close the keypad device.
+ * Nothing is done in this function, since every thing is taken care in
+ * __exit function.
+ *
+ * @param dev Pointer to device inode
+ *
+ */
+static void mc9s08dz60kpp_close(struct input_dev *dev)
+{
+}
+
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration. Otherwise returns
+ * specific error code.
+ */
+static int mc9s08dz60kpp_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ int retval;
+
+ retval = mcu_pmic_write_reg(REG_MCU_KPD_CONTROL, 0x1, 0x1);
+ if (retval != 0) {
+ pr_info("mc9s08dz60 keypad: mcu not detected!\n");
+ return retval;
+ }
+
+ irq = platform_get_irq(pdev, 0);
+ retval = request_irq(irq, mc9s08dz60kpp_interrupt,
+ 0, MOD_NAME, MOD_NAME);
+ if (retval) {
+ pr_debug("KPP: request_irq(%d) returned error %d\n",
+ irq, retval);
+ return retval;
+ }
+ keypad_irq = irq;
+
+ mc9s08dz60kbd_dev = input_allocate_device();
+ if (!mc9s08dz60kbd_dev) {
+ pr_info(KERN_ERR "mc9s08dz60kbd_dev: \
+ not enough memory for input device\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ mc9s08dz60kbd_dev->name = "mc9s08dz60kpd";
+ mc9s08dz60kbd_dev->id.bustype = BUS_HOST;
+ mc9s08dz60kbd_dev->open = mc9s08dz60kpp_open;
+ mc9s08dz60kbd_dev->close = mc9s08dz60kpp_close;
+
+ retval = input_register_device(mc9s08dz60kbd_dev);
+ if (retval < 0) {
+ pr_info(KERN_ERR
+ "mc9s08dz60kbd_dev: failed to register input device\n");
+ goto err2;
+ }
+
+ __set_bit(EV_KEY, mc9s08dz60kbd_dev->evbit);
+
+ for (i = 0; i < keycodes_size; i++)
+ __set_bit(key_code_map[i], mc9s08dz60kbd_dev->keybit);
+
+ device_init_wakeup(&pdev->dev, 1);
+
+ pr_info("mc9s08dz60 keypad probed\n");
+
+ return 0;
+
+err2:
+ input_free_device(mc9s08dz60kbd_dev);
+err1:
+ free_irq(irq, MOD_NAME);
+ return retval;
+}
+
+/*!
+ * Dissociates the driver from the kpp device.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mc9s08dz60kpp_remove(struct platform_device *pdev)
+{
+ free_irq(keypad_irq, MOD_NAME);
+ input_unregister_device(mc9s08dz60kbd_dev);
+
+ if (mc9s08dz60kbd_dev)
+ input_free_device(mc9s08dz60kbd_dev);
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mc9s08dz60kpd_driver = {
+ .driver = {
+ .name = "mc9s08dz60keypad",
+ .bus = &platform_bus_type,
+ },
+ .probe = mc9s08dz60kpp_probe,
+ .remove = mc9s08dz60kpp_remove
+};
+
+static int __init mc9s08dz60kpp_init(void)
+{
+ pr_info(KERN_INFO "mc9s08dz60 keypad loaded\n");
+ platform_driver_register(&mc9s08dz60kpd_driver);
+ return 0;
+}
+
+static void __exit mc9s08dz60kpp_cleanup(void)
+{
+ platform_driver_unregister(&mc9s08dz60kpd_driver);
+}
+
+module_init(mc9s08dz60kpp_init);
+module_exit(mc9s08dz60kpp_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Keypad Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/mpr084.c b/drivers/input/keyboard/mpr084.c
new file mode 100644
index 000000000000..4b0c4883751e
--- /dev/null
+++ b/drivers/input/keyboard/mpr084.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2008-2009 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 <linux/regulator/consumer.h>
+#include <asm/mach/irq.h>
+#include <mach/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;
+ int opened;
+};
+
+static int kpstatus[KEY_COUNT];
+static struct mxc_keyp_platform_data *keypad;
+static const unsigned short *mxckpd_keycodes;
+static struct regulator *vdd_reg;
+
+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 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 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;
+}
+
+/*!
+ * This function puts the Keypad controller in low-power mode/state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return The function always returns 0.
+ */
+static int mpr084_suspend(struct i2c_client *client, pm_message_t state)
+{
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ if (!IS_ERR(d->tstask) && d->opened)
+ kthread_stop(d->tstask);
+
+ return 0;
+}
+
+/*!
+ * This function brings the Keypad controller back from low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mpr084_resume(struct i2c_client *client)
+{
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ if (d->opened)
+ d->tstask = kthread_run(mpr084ts_thread, d, DRIVER_NAME "kpd");
+
+ return 0;
+}
+
+static int mpr084_idev_open(struct input_dev *idev)
+{
+ struct mpr084_data *d = input_get_drvdata(idev);
+ int ret = 0;
+
+ d->tstask = kthread_run(mpr084ts_thread, d, DRIVER_NAME "kpd");
+ if (IS_ERR(d->tstask))
+ ret = PTR_ERR(d->tstask);
+ else
+ d->opened++;
+ return ret;
+}
+
+static void mpr084_idev_close(struct input_dev *idev)
+{
+ struct mpr084_data *d = input_get_drvdata(idev);
+
+ if (!IS_ERR(d->tstask))
+ kthread_stop(d->tstask);
+ if (d->opened > 0)
+ d->opened--;
+}
+
+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);
+ set_irq_wake(data->kpirq, 1);
+ } else {
+ printk(KERN_ERR "%s: cannot grab irq %d\n",
+ __func__, data->kpirq);
+ }
+
+ }
+ idev = input_allocate_device();
+ data->idev = idev;
+ input_set_drvdata(idev, 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)
+{
+ struct mpr084_data *d = i2c_get_clientdata(client);
+
+ free_irq(d->kpirq, d);
+ input_unregister_device(d->idev);
+ if (keypad->inactive)
+ keypad->inactive();
+
+ /*Disable the Regulator*/
+ if (keypad->vdd_reg) {
+ regulator_disable(vdd_reg);
+ regulator_put(vdd_reg);
+ }
+
+ return 0;
+}
+
+static int mpr084_configure(struct mpr084_data *data)
+{
+ int ret = 0, regValue = 0;
+
+ ret = mpr084_write_register(data, MPR084_TPC_ADDR, 0x1d);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR1_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR2_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR3_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR4_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR5_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR6_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR7_ADDR, 0x10);
+ if (ret < 0)
+ goto err;
+ ret = mpr084_write_register(data, MPR084_STR8_ADDR, 0x10);
+ 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, 0x02);
+ if (ret < 0)
+ goto err;
+
+ /* master tick period*/
+ ret = mpr084_write_register(data, MPR084_MTC_ADDR, 0x05);
+ if (ret < 0)
+ goto err;
+
+
+ /*Sample period */
+ ret = mpr084_write_register(data, MPR084_TASP_ADDR, 0x02);
+ if (ret < 0)
+ goto err;
+
+
+ /* disable sournder*/
+ ret = mpr084_write_register(data, MPR084_SC_ADDR, 0x00);
+ if (ret < 0)
+ goto err;
+
+ /* stuck key timeout */
+ ret = mpr084_write_register(data, MPR084_SKT_ADDR, 0x01);
+ if (ret < 0)
+ goto err;
+
+ /*enabled IRQEN, RUNE, IRQR */
+ ret = mpr084_read_register(data, MPR084_CONFIG_ADDR, &regValue);
+ if (ret < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad CONFIGADDR register \n\n",
+ __func__);
+ goto err;
+ }
+ regValue |= 0x03;
+ ret = mpr084_write_register(data, MPR084_CONFIG_ADDR, regValue);
+ if (ret < 0)
+ goto err;
+ return ret;
+err:
+ return -ENODEV;
+}
+
+static int mpr084_i2c_probe(struct i2c_client *client, const struct i2c_device_id *id)
+{
+ struct mpr084_data *data;
+ int err = 0, i = 0;
+#if DEBUG
+ int regValue = 0;
+#endif
+ 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();
+
+ /*Enable the Regulator*/
+ if (keypad && keypad->vdd_reg) {
+ vdd_reg = regulator_get(&client->dev, keypad->vdd_reg);
+ if (!IS_ERR(vdd_reg))
+ regulator_enable(vdd_reg);
+ else
+ vdd_reg = NULL;
+ } else
+ vdd_reg = NULL;
+
+ 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;
+ }
+
+#if DEBUG
+ for (i = MPR084_ADDR_MINI; i <= MPR084_ADDR_MAX; i++) {
+ err = mpr084_read_register(data, i, &regValue);
+ if (err < 0) {
+ printk(KERN_ERR
+ "%s: Err in reading keypad CONFIGADDR register \n\n",
+ __func__);
+ goto exit_free;
+ }
+ printk("MPR084 Register id: %d, Value:%d \n", i, regValue);
+
+ }
+#endif
+ memset(kpstatus, 0, sizeof(kpstatus));
+ printk(KERN_INFO "%s: Device Attached\n", __func__);
+ return 0;
+exit_free:
+ /*disable the Regulator*/
+ if (vdd_reg) {
+ regulator_disable(vdd_reg);
+ regulator_put(vdd_reg);
+ vdd_reg = NULL;
+ }
+ kfree(data);
+ return err;
+}
+
+static const struct i2c_device_id mpr084_id[] = {
+ { "mpr084", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(i2c, mpr084_id);
+
+static struct i2c_driver mpr084_driver = {
+ .driver = {
+ .name = DRIVER_NAME,
+ },
+ .probe = mpr084_i2c_probe,
+ .remove = mpr084_i2c_remove,
+ .suspend = mpr084_suspend,
+ .resume = mpr084_resume,
+ .command = NULL,
+ .id_table = mpr084_id,
+};
+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/drivers/input/keyboard/mxc_keyb.c b/drivers/input/keyboard/mxc_keyb.c
new file mode 100644
index 000000000000..033713cbdfdf
--- /dev/null
+++ b/drivers/input/keyboard/mxc_keyb.c
@@ -0,0 +1,1202 @@
+/*
+ * Copyright 2004-2010 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 mxc_keyb.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC keypad port.
+ *
+ * The keypad driver is designed as a standard Input driver which interacts
+ * with low level keypad port hardware. Upon opening, the Keypad driver
+ * initializes the keypad port. When the keypad interrupt happens the driver
+ * calles keypad polling timer and scans the keypad matrix for key
+ * press/release. If all key press/release happened it comes out of timer and
+ * waits for key press interrupt. The scancode for key press and release events
+ * are passed to Input subsytem.
+ *
+ * @ingroup keypad
+ */
+
+/*!
+ * Comment KPP_DEBUG to disable debug messages
+ */
+#define KPP_DEBUG 0
+
+#if KPP_DEBUG
+#define DEBUG
+#include <linux/kernel.h>
+#endif
+
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <mach/hardware.h>
+#include <linux/kd.h>
+#include <linux/fs.h>
+#include <linux/kbd_kern.h>
+#include <linux/ioctl.h>
+#include <linux/poll.h>
+#include <linux/interrupt.h>
+#include <linux/timer.h>
+#include <linux/input.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <asm/mach/keypad.h>
+
+/*!
+ * Keypad Module Name
+ */
+#define MOD_NAME "mxckpd"
+
+/*!
+ * XLATE mode selection
+ */
+#define KEYPAD_XLATE 0
+
+/*!
+ * RAW mode selection
+ */
+#define KEYPAD_RAW 1
+
+/*!
+ * Maximum number of keys.
+ */
+#define MAXROW 8
+#define MAXCOL 8
+#define MXC_MAXKEY (MAXROW * MAXCOL)
+
+/*!
+ * This define indicates break scancode for every key release. A constant
+ * of 128 is added to the key press scancode.
+ */
+#define MXC_KEYRELEASE 128
+
+/*
+ * _reg_KPP_KPCR _reg_KPP_KPSR _reg_KPP_KDDR _reg_KPP_KPDR
+ * The offset of Keypad Control Register Address
+ */
+#define KPCR 0x00
+
+/*
+ * The offset of Keypad Status Register Address
+ */
+#define KPSR 0x02
+
+/*
+ * The offset of Keypad Data Direction Address
+ */
+#define KDDR 0x04
+
+/*
+ * The offset of Keypad Data Register
+ */
+#define KPDR 0x06
+
+/*
+ * Key Press Interrupt Status bit
+ */
+#define KBD_STAT_KPKD 0x01
+
+/*
+ * Key Release Interrupt Status bit
+ */
+#define KBD_STAT_KPKR 0x02
+
+/*
+ * Key Depress Synchronizer Chain Status bit
+ */
+#define KBD_STAT_KDSC 0x04
+
+/*
+ * Key Release Synchronizer Status bit
+ */
+#define KBD_STAT_KRSS 0x08
+
+/*
+ * Key Depress Interrupt Enable Status bit
+ */
+#define KBD_STAT_KDIE 0x100
+
+/*
+ * Key Release Interrupt Enable
+ */
+#define KBD_STAT_KRIE 0x200
+
+/*
+ * Keypad Clock Enable
+ */
+#define KBD_STAT_KPPEN 0x400
+
+/*!
+ * Buffer size of keypad queue. Should be a power of 2.
+ */
+#define KPP_BUF_SIZE 128
+
+/*!
+ * Test whether bit is set for integer c
+ */
+#define TEST_BIT(c, n) ((c) & (0x1 << (n)))
+
+/*!
+ * Set nth bit in the integer c
+ */
+#define BITSET(c, n) ((c) | (1 << (n)))
+
+/*!
+ * Reset nth bit in the integer c
+ */
+#define BITRESET(c, n) ((c) & ~(1 << (n)))
+
+/*!
+ * This enum represents the keypad state machine to maintain debounce logic
+ * for key press/release.
+ */
+enum KeyState {
+
+ /*!
+ * Key press state.
+ */
+ KStateUp,
+
+ /*!
+ * Key press debounce state.
+ */
+ KStateFirstDown,
+
+ /*!
+ * Key release state.
+ */
+ KStateDown,
+
+ /*!
+ * Key release debounce state.
+ */
+ KStateFirstUp
+};
+
+/*!
+ * Keypad Private Data Structure
+ */
+struct keypad_priv {
+
+ /*!
+ * Keypad state machine.
+ */
+ enum KeyState iKeyState;
+
+ /*!
+ * Number of rows configured in the keypad matrix
+ */
+ unsigned long kpp_rows;
+
+ /*!
+ * Number of Columns configured in the keypad matrix
+ */
+ unsigned long kpp_cols;
+
+ /*!
+ * Timer used for Keypad polling.
+ */
+ struct timer_list poll_timer;
+
+ /*!
+ * The base address
+ */
+ void __iomem *base;
+};
+/*!
+ * This structure holds the keypad private data structure.
+ */
+static struct keypad_priv kpp_dev;
+
+/*! Indicates if the key pad device is enabled. */
+static unsigned int key_pad_enabled;
+
+/*! Input device structure. */
+static struct input_dev *mxckbd_dev = NULL;
+
+/*! KPP clock handle. */
+static struct clk *kpp_clk;
+
+/*! This static variable indicates whether a key event is pressed/released. */
+static unsigned short KPress;
+
+/*! cur_rcmap and prev_rcmap array is used to detect key press and release. */
+static unsigned short *cur_rcmap; /* max 64 bits (8x8 matrix) */
+static unsigned short *prev_rcmap;
+
+/*!
+ * Debounce polling period(10ms) in system ticks.
+ */
+static unsigned short KScanRate = (10 * HZ) / 1000;
+
+static struct keypad_data *keypad;
+
+static int has_leaning_key;
+/*!
+ * These arrays are used to store press and release scancodes.
+ */
+static short **press_scancode;
+static short **release_scancode;
+
+static const unsigned short *mxckpd_keycodes;
+static unsigned short mxckpd_keycodes_size;
+
+#define press_left_code 30
+#define press_right_code 29
+#define press_up_code 28
+#define press_down_code 27
+
+#define rel_left_code 158
+#define rel_right_code 157
+#define rel_up_code 156
+#define rel_down_code 155
+/*!
+ * 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);
+
+/*!
+ * This function is called for generating scancodes for key press and
+ * release on keypad for the board.
+ *
+ * @param row Keypad row pressed on the keypad matrix.
+ * @param col Keypad col pressed on the keypad matrix.
+ * @param press Indicated key press/release.
+ *
+ * @return Key press/release Scancode.
+ */
+static signed short mxc_scan_matrix_leaning_key(int row, int col, int press)
+{
+ static unsigned first_row;
+ static unsigned first_set = 0, flag = 0;
+ signed short scancode = -1;
+
+ if (press) {
+ if ((3 == col) && ((3 == row) ||
+ (4 == row) || (5 == row) || (6 == row))) {
+ if (first_set == 0) {
+ first_set = 1;
+ first_row = row;
+ } else {
+ first_set = 0;
+ if (((first_row == 6) || (first_row == 3))
+ && ((row == 6) || (row == 3)))
+ scancode = press_down_code;
+ else if (((first_row == 3) || (first_row == 5))
+ && ((row == 3) || (row == 5)))
+ scancode = press_left_code;
+ else if (((first_row == 6) || (first_row == 4))
+ && ((row == 6) || (row == 4)))
+ scancode = press_right_code;
+ else if (((first_row == 4) || (first_row == 5))
+ && ((row == 4) || (row == 5)))
+ scancode = press_up_code;
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ } else {
+ /*
+ * check for other keys only
+ * if the cursor key presses
+ * are not detected may be
+ * this needs better logic
+ */
+ if ((0 == (cur_rcmap[3] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[4] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
+ scancode = ((col * kpp_dev.kpp_rows) + row);
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ flag = 1;
+ pr_debug("Press (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ }
+ } else {
+ if ((flag == 0) && (3 == col)
+ && ((3 == row) || (4 == row) || (5 == row)
+ || (6 == row))) {
+ if (first_set == 0) {
+ first_set = 1;
+ first_row = row;
+ } else {
+ first_set = 0;
+ if (((first_row == 6) || (first_row == 3))
+ && ((row == 6) || (row == 3)))
+ scancode = rel_down_code;
+ else if (((first_row == 3) || (first_row == 5))
+ && ((row == 3) || (row == 5)))
+ scancode = rel_left_code;
+ else if (((first_row == 6) || (first_row == 4))
+ && ((row == 6) || (row == 4)))
+ scancode = rel_right_code;
+ else if (((first_row == 4) || (first_row == 5))
+ && ((row == 4) || (row == 5)))
+ scancode = rel_up_code;
+ KPress = 0;
+ kpp_dev.iKeyState = KStateDown;
+ pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ } else {
+ /*
+ * check for other keys only
+ * if the cursor key presses
+ * are not detected may be
+ * this needs better logic
+ */
+ if ((0 == (prev_rcmap[3] & BITSET(0, 3))) &&
+ (0 == (prev_rcmap[4] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[5] & BITSET(0, 3))) &&
+ (0 == (cur_rcmap[6] & BITSET(0, 3)))) {
+ scancode = ((col * kpp_dev.kpp_rows) + row) +
+ MXC_KEYRELEASE;
+ KPress = 0;
+ flag = 0;
+ kpp_dev.iKeyState = KStateDown;
+ pr_debug("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ }
+ }
+ }
+ return scancode;
+}
+
+/*!
+ * This function is called to scan the keypad matrix to find out the key press
+ * and key release events. Make scancode and break scancode are generated for
+ * key press and key release events.
+ *
+ * The following scanning sequence are done for
+ * keypad row and column scanning,
+ * -# Write 1's to KPDR[15:8], setting column data to 1's
+ * -# Configure columns as totem pole outputs(for quick discharging of keypad
+ * capacitance)
+ * -# Configure columns as open-drain
+ * -# Write a single column to 0, others to 1.
+ * -# Sample row inputs and save data. Multiple key presses can be detected on
+ * a single column.
+ * -# Repeat steps the above steps for remaining columns.
+ * -# Return all columns to 0 in preparation for standby mode.
+ * -# Clear KPKD and KPKR status bit(s) by writing to a 1,
+ * Set the KPKR synchronizer chain by writing "1" to KRSS register,
+ * Clear the KPKD synchronizer chain by writing "1" to KDSC register
+ *
+ * @result Number of key pressed/released.
+ */
+static int mxc_kpp_scan_matrix(void)
+{
+ unsigned short reg_val;
+ int col, row;
+ short scancode = 0;
+ int keycnt = 0; /* How many keys are still pressed */
+
+ /*
+ * wmb() linux kernel function which guarantees orderings in write
+ * operations
+ */
+ wmb();
+
+ /* save cur keypad matrix to prev */
+
+ memcpy(prev_rcmap, cur_rcmap, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+ memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+
+ for (col = 0; col < kpp_dev.kpp_cols; col++) { /* Col */
+ /* 2. Write 1.s to KPDR[15:8] setting column data to 1.s */
+ reg_val = __raw_readw(kpp_dev.base + KPDR);
+ reg_val |= 0xff00;
+ __raw_writew(reg_val, kpp_dev.base + KPDR);
+
+ /*
+ * 3. Configure columns as totem pole outputs(for quick
+ * discharging of keypad capacitance)
+ */
+ reg_val = __raw_readw(kpp_dev.base + KPCR);
+ reg_val &= 0x00ff;
+ __raw_writew(reg_val, kpp_dev.base + KPCR);
+
+ udelay(2);
+
+ /*
+ * 4. Configure columns as open-drain
+ */
+ reg_val = __raw_readw(kpp_dev.base + KPCR);
+ reg_val |= ((1 << kpp_dev.kpp_cols) - 1) << 8;
+ __raw_writew(reg_val, kpp_dev.base + KPCR);
+
+ /*
+ * 5. Write a single column to 0, others to 1.
+ * 6. Sample row inputs and save data. Multiple key presses
+ * can be detected on a single column.
+ * 7. Repeat steps 2 - 6 for remaining columns.
+ */
+
+ /* Col bit starts at 8th bit in KPDR */
+ reg_val = __raw_readw(kpp_dev.base + KPDR);
+ reg_val &= ~(1 << (8 + col));
+ __raw_writew(reg_val, kpp_dev.base + KPDR);
+
+ /* Delay added to avoid propagating the 0 from column to row
+ * when scanning. */
+
+ udelay(5);
+
+ /* Read row input */
+ reg_val = __raw_readw(kpp_dev.base + KPDR);
+ for (row = 0; row < kpp_dev.kpp_rows; row++) { /* sample row */
+ if (TEST_BIT(reg_val, row) == 0) {
+ cur_rcmap[row] = BITSET(cur_rcmap[row], col);
+ keycnt++;
+ }
+ }
+ }
+
+ /*
+ * 8. Return all columns to 0 in preparation for standby mode.
+ * 9. Clear KPKD and KPKR status bit(s) by writing to a .1.,
+ * set the KPKR synchronizer chain by writing "1" to KRSS register,
+ * clear the KPKD synchronizer chain by writing "1" to KDSC register
+ */
+ reg_val = 0x00;
+ __raw_writew(reg_val, kpp_dev.base + KPDR);
+ reg_val = __raw_readw(kpp_dev.base + KPDR);
+ reg_val = __raw_readw(kpp_dev.base + KPSR);
+ reg_val |= KBD_STAT_KPKD | KBD_STAT_KPKR | KBD_STAT_KRSS |
+ KBD_STAT_KDSC;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+
+ /* Check key press status change */
+
+ /*
+ * prev_rcmap array will contain the previous status of the keypad
+ * matrix. cur_rcmap array will contains the present status of the
+ * keypad matrix. If a bit is set in the array, that (row, col) bit is
+ * pressed, else it is not pressed.
+ *
+ * XORing these two variables will give us the change in bit for
+ * particular row and column. If a bit is set in XOR output, then that
+ * (row, col) has a change of status from the previous state. From
+ * the diff variable the key press and key release of row and column
+ * are found out.
+ *
+ * If the key press is determined then scancode for key pressed
+ * can be generated using the following statement:
+ * scancode = ((row * 8) + col);
+ *
+ * If the key release is determined then scancode for key release
+ * can be generated using the following statement:
+ * scancode = ((row * 8) + col) + MXC_KEYRELEASE;
+ */
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ unsigned char diff;
+
+ /*
+ * Calculate the change in the keypad row status
+ */
+ diff = prev_rcmap[row] ^ cur_rcmap[row];
+
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((diff >> col) & 0x1) {
+ /* There is a status change on col */
+ if ((prev_rcmap[row] & BITSET(0, col)) == 0) {
+ /*
+ * Previous state is 0, so now
+ * a key is pressed
+ */
+ if (has_leaning_key) {
+ scancode =
+ mxc_scan_matrix_leaning_key
+ (row, col, 1);
+ } else {
+ scancode =
+ ((row * kpp_dev.kpp_cols) +
+ col);
+ KPress = 1;
+ kpp_dev.iKeyState = KStateUp;
+ }
+ pr_debug("Press (%d, %d) scan=%d "
+ "Kpress=%d\n",
+ row, col, scancode, KPress);
+ press_scancode[row][col] =
+ (short)scancode;
+ } else {
+ /*
+ * Previous state is not 0, so
+ * now a key is released
+ */
+ if (has_leaning_key) {
+ scancode =
+ mxc_scan_matrix_leaning_key
+ (row, col, 0);
+ } else {
+ scancode =
+ (row * kpp_dev.kpp_cols) +
+ col + MXC_KEYRELEASE;
+ KPress = 0;
+ kpp_dev.iKeyState = KStateDown;
+ }
+
+ pr_debug
+ ("Release (%d, %d) scan=%d Kpress=%d\n",
+ row, col, scancode, KPress);
+ release_scancode[row][col] =
+ (short)scancode;
+ keycnt++;
+ }
+ }
+ }
+ }
+
+ /*
+ * This switch case statement is the
+ * implementation of state machine of debounce
+ * logic for key press/release.
+ * The explaination of state machine is as
+ * follows:
+ *
+ * KStateUp State:
+ * This is in intial state of the state machine
+ * this state it checks for any key presses.
+ * The key press can be checked using the
+ * variable KPress. If KPress is set, then key
+ * press is identified and switches the to
+ * KStateFirstDown state for key press to
+ * debounce.
+ *
+ * KStateFirstDown:
+ * After debounce delay(10ms), if the KPress is
+ * still set then pass scancode generated to
+ * input device and change the state to
+ * KStateDown, else key press debounce is not
+ * satisfied so change the state to KStateUp.
+ *
+ * KStateDown:
+ * In this state it checks for any key release.
+ * If KPress variable is cleared, then key
+ * release is indicated and so, switch the
+ * state to KStateFirstUp else to state
+ * KStateDown.
+ *
+ * KStateFirstUp:
+ * After debounce delay(10ms), if the KPress is
+ * still reset then pass the key release
+ * scancode to input device and change
+ * the state to KStateUp else key release is
+ * not satisfied so change the state to
+ * KStateDown.
+ */
+ switch (kpp_dev.iKeyState) {
+ case KStateUp:
+ if (KPress) {
+ /* First Down (must debounce). */
+ kpp_dev.iKeyState = KStateFirstDown;
+ } else {
+ /* Still UP.(NO Changes) */
+ kpp_dev.iKeyState = KStateUp;
+ }
+ break;
+
+ case KStateFirstDown:
+ if (KPress) {
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((press_scancode[row][col] != -1)) {
+ /* Still Down, so add scancode */
+ scancode =
+ press_scancode[row][col];
+ input_event(mxckbd_dev, EV_KEY,
+ mxckpd_keycodes
+ [scancode], 1);
+ if (mxckpd_keycodes[scancode] ==
+ KEY_LEFTSHIFT) {
+ input_event(mxckbd_dev,
+ EV_KEY,
+ KEY_3, 1);
+ }
+ kpp_dev.iKeyState = KStateDown;
+ press_scancode[row][col] = -1;
+ }
+ }
+ }
+ } else {
+ /* Just a bounce */
+ kpp_dev.iKeyState = KStateUp;
+ }
+ break;
+
+ case KStateDown:
+ if (KPress) {
+ /* Still down (no change) */
+ kpp_dev.iKeyState = KStateDown;
+ } else {
+ /* First Up. Must debounce */
+ kpp_dev.iKeyState = KStateFirstUp;
+ }
+ break;
+
+ case KStateFirstUp:
+ if (KPress) {
+ /* Just a bounce */
+ kpp_dev.iKeyState = KStateDown;
+ } else {
+ for (row = 0; row < kpp_dev.kpp_rows; row++) {
+ for (col = 0; col < kpp_dev.kpp_cols; col++) {
+ if ((release_scancode[row][col] != -1)) {
+ scancode =
+ release_scancode[row][col];
+ scancode =
+ scancode - MXC_KEYRELEASE;
+ input_event(mxckbd_dev, EV_KEY,
+ mxckpd_keycodes
+ [scancode], 0);
+ if (mxckpd_keycodes[scancode] ==
+ KEY_LEFTSHIFT) {
+ input_event(mxckbd_dev,
+ EV_KEY,
+ KEY_3, 0);
+ }
+ kpp_dev.iKeyState = KStateUp;
+ release_scancode[row][col] = -1;
+ }
+ }
+ }
+ }
+ break;
+
+ default:
+ return -EBADRQC;
+ break;
+ }
+
+ return keycnt;
+}
+
+/*!
+ * This function is called to start the timer for scanning the keypad if there
+ * is any key press. Currently this interval is set to 10 ms. When there are
+ * no keys pressed on the keypad we return back, waiting for a keypad key
+ * press interrupt.
+ *
+ * @param data Opaque data passed back by kernel. Not used.
+ */
+static void mxc_kpp_handle_timer(unsigned long data)
+{
+ unsigned short reg_val;
+ int i;
+
+ if (key_pad_enabled == 0) {
+ return;
+ }
+ if (mxc_kpp_scan_matrix() == 0) {
+ /*
+ * Stop scanning and wait for interrupt.
+ * Enable press interrupt and disable release interrupt.
+ */
+ __raw_writew(0x00FF, kpp_dev.base + KPDR);
+ reg_val = __raw_readw(kpp_dev.base + KPSR);
+ reg_val |= (KBD_STAT_KPKR | KBD_STAT_KPKD);
+ reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+
+ /*
+ * No more keys pressed... make sure unwanted key codes are
+ * not given upstairs
+ */
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ memset(press_scancode[i], -1,
+ sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+ memset(release_scancode[i], -1,
+ sizeof(release_scancode[0][0]) *
+ kpp_dev.kpp_cols);
+ }
+ return;
+ }
+
+ /*
+ * There are still some keys pressed, continue to scan.
+ * We shall scan again in 10 ms. This has to be tuned according
+ * to the requirement.
+ */
+ kpp_dev.poll_timer.expires = jiffies + KScanRate;
+ kpp_dev.poll_timer.function = mxc_kpp_handle_timer;
+ add_timer(&kpp_dev.poll_timer);
+}
+
+/*!
+ * This function is the keypad Interrupt handler.
+ * This function checks for keypad status register (KPSR) for key press
+ * and interrupt. If key press interrupt has occurred, then the key
+ * press interrupt in the KPSR are disabled.
+ * It then calls mxc_kpp_scan_matrix to check for any key pressed/released.
+ * If any key is found to be pressed, then a timer is set to call
+ * mxc_kpp_scan_matrix function for every 10 ms.
+ *
+ * @param irq The Interrupt number
+ * @param dev_id Driver private data
+ *
+ * @result The function returns \b IRQ_RETVAL(1) if interrupt was handled,
+ * returns \b IRQ_RETVAL(0) if the interrupt was not handled.
+ * \b IRQ_RETVAL is defined in include/linux/interrupt.h.
+ */
+static irqreturn_t mxc_kpp_interrupt(int irq, void *dev_id)
+{
+ unsigned short reg_val;
+
+ /* Delete the polling timer */
+ del_timer(&kpp_dev.poll_timer);
+ reg_val = __raw_readw(kpp_dev.base + KPSR);
+
+ /* Check if it is key press interrupt */
+ if (reg_val & KBD_STAT_KPKD) {
+ /*
+ * Disable key press(KDIE status bit) interrupt
+ */
+ reg_val &= ~KBD_STAT_KDIE;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+ } else {
+ /* spurious interrupt */
+ return IRQ_RETVAL(0);
+ }
+ /*
+ * Check if any keys are pressed, if so start polling.
+ */
+ mxc_kpp_handle_timer(0);
+
+ return IRQ_RETVAL(1);
+}
+
+/*!
+ * This function is called when the keypad driver is opened.
+ * Since keypad initialization is done in __init, nothing is done in open.
+ *
+ * @param dev Pointer to device inode
+ *
+ * @result The function always return 0
+ */
+static int mxc_kpp_open(struct input_dev *dev)
+{
+ return 0;
+}
+
+/*!
+ * This function is called close the keypad device.
+ * Nothing is done in this function, since every thing is taken care in
+ * __exit function.
+ *
+ * @param dev Pointer to device inode
+ *
+ */
+static void mxc_kpp_close(struct input_dev *dev)
+{
+}
+
+#ifdef CONFIG_PM
+/*!
+ * This function puts the Keypad controller in low-power mode/state.
+ * If Keypad is enabled as a wake source(i.e. it can resume the system
+ * from suspend mode), the Keypad controller doesn't enter low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to suspend
+ * @param state the power state the device is entering
+ *
+ * @return return -1 when the keypad is pressed. Otherwise, return 0
+ */
+static int mxc_kpp_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ /* When the keypad is still pressed, clean up registers and timers */
+ if (timer_pending(&kpp_dev.poll_timer))
+ return -1;
+
+ if (device_may_wakeup(&pdev->dev)) {
+ enable_irq_wake(keypad->irq);
+ } else {
+ disable_irq(keypad->irq);
+ key_pad_enabled = 0;
+ clk_disable(kpp_clk);
+ gpio_keypad_inactive();
+ }
+
+ return 0;
+}
+
+/*!
+ * This function brings the Keypad controller back from low-power state.
+ * If Keypad is enabled as a wake source(i.e. it can resume the system
+ * from suspend mode), the Keypad controller doesn't enter low-power state.
+ *
+ * @param pdev the device structure used to give information on Keypad
+ * to resume
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_kpp_resume(struct platform_device *pdev)
+{
+ if (device_may_wakeup(&pdev->dev)) {
+ disable_irq_wake(keypad->irq);
+ } else {
+ gpio_keypad_active();
+ clk_enable(kpp_clk);
+ key_pad_enabled = 1;
+ enable_irq(keypad->irq);
+ }
+
+ return 0;
+}
+
+#else
+#define mxc_kpp_suspend NULL
+#define mxc_kpp_resume NULL
+#endif /* CONFIG_PM */
+
+/*!
+ * This function is called to free the allocated memory for local arrays
+ */
+static void mxc_kpp_free_allocated(void)
+{
+
+ int i;
+
+ if (press_scancode) {
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ if (press_scancode[i])
+ kfree(press_scancode[i]);
+ }
+ kfree(press_scancode);
+ }
+
+ if (release_scancode) {
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ if (release_scancode[i])
+ kfree(release_scancode[i]);
+ }
+ kfree(release_scancode);
+ }
+
+ if (cur_rcmap)
+ kfree(cur_rcmap);
+
+ if (prev_rcmap)
+ kfree(prev_rcmap);
+
+ if (mxckbd_dev)
+ input_free_device(mxckbd_dev);
+}
+
+/*!
+ * This function is called during the driver binding process.
+ *
+ * @param pdev the device structure used to store device specific
+ * information that is used by the suspend, resume and remove
+ * functions.
+ *
+ * @return The function returns 0 on successful registration. Otherwise returns
+ * specific error code.
+ */
+static int mxc_kpp_probe(struct platform_device *pdev)
+{
+ int i, irq;
+ int retval;
+ unsigned int reg_val;
+ struct resource *res;
+
+ keypad = (struct keypad_data *)pdev->dev.platform_data;
+
+ kpp_dev.kpp_cols = keypad->colmax;
+ kpp_dev.kpp_rows = keypad->rowmax;
+ key_pad_enabled = 0;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res)
+ return -ENODEV;
+
+ kpp_dev.base = ioremap(res->start, res->end - res->start + 1);
+ if (!kpp_dev.base)
+ return -ENOMEM;
+
+ irq = platform_get_irq(pdev, 0);
+ keypad->irq = irq;
+
+ /* Enable keypad clock */
+ kpp_clk = clk_get(&pdev->dev, "kpp_clk");
+ clk_enable(kpp_clk);
+
+ /* IOMUX configuration for keypad */
+ gpio_keypad_active();
+
+ /* Configure keypad */
+
+ /* Enable number of rows in keypad (KPCR[7:0])
+ * Configure keypad columns as open-drain (KPCR[15:8])
+ *
+ * Configure the rows/cols in KPP
+ * LSB nibble in KPP is for 8 rows
+ * MSB nibble in KPP is for 8 cols
+ */
+ reg_val = __raw_readw(kpp_dev.base + KPCR);
+ reg_val |= (1 << keypad->rowmax) - 1; /* LSB */
+ reg_val |= ((1 << keypad->colmax) - 1) << 8; /* MSB */
+ __raw_writew(reg_val, kpp_dev.base + KPCR);
+
+ /* Write 0's to KPDR[15:8] */
+ reg_val = __raw_readw(kpp_dev.base + KPDR);
+ reg_val &= 0x00ff;
+ __raw_writew(reg_val, kpp_dev.base + KPDR);
+
+ /* Configure columns as output, rows as input (KDDR[15:0]) */
+ reg_val = __raw_readw(kpp_dev.base + KDDR);
+ reg_val |= 0xff00;
+ reg_val &= 0xff00;
+ __raw_writew(reg_val, kpp_dev.base + KDDR);
+
+ reg_val = __raw_readw(kpp_dev.base + KPSR);
+ reg_val &= ~(KBD_STAT_KPKR | KBD_STAT_KPKD);
+ reg_val |= KBD_STAT_KPKD;
+ reg_val |= KBD_STAT_KRSS | KBD_STAT_KDSC;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+
+ has_leaning_key = keypad->learning;
+ mxckpd_keycodes = keypad->matrix;
+ mxckpd_keycodes_size = keypad->rowmax * keypad->colmax;
+
+ if ((keypad->matrix == (void *)0)
+ || (mxckpd_keycodes_size == 0)) {
+ retval = -ENODEV;
+ goto err1;
+ }
+
+ mxckbd_dev = input_allocate_device();
+ if (!mxckbd_dev) {
+ printk(KERN_ERR
+ "mxckbd_dev: not enough memory for input device\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ mxckbd_dev->keycode = (void *)mxckpd_keycodes;
+ mxckbd_dev->keycodesize = sizeof(mxckpd_keycodes[0]);
+ mxckbd_dev->keycodemax = mxckpd_keycodes_size;
+ mxckbd_dev->name = "mxckpd";
+ mxckbd_dev->id.bustype = BUS_HOST;
+ mxckbd_dev->open = mxc_kpp_open;
+ mxckbd_dev->close = mxc_kpp_close;
+
+ retval = input_register_device(mxckbd_dev);
+ if (retval < 0) {
+ printk(KERN_ERR
+ "mxckbd_dev: failed to register input device\n");
+ goto err2;
+ }
+
+ /* allocate required memory */
+ press_scancode = kmalloc(kpp_dev.kpp_rows * sizeof(press_scancode[0]),
+ GFP_KERNEL);
+ release_scancode =
+ kmalloc(kpp_dev.kpp_rows * sizeof(release_scancode[0]), GFP_KERNEL);
+
+ if (!press_scancode || !release_scancode) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ press_scancode[i] = kmalloc(kpp_dev.kpp_cols
+ * sizeof(press_scancode[0][0]),
+ GFP_KERNEL);
+ release_scancode[i] =
+ kmalloc(kpp_dev.kpp_cols * sizeof(release_scancode[0][0]),
+ GFP_KERNEL);
+
+ if (!press_scancode[i] || !release_scancode[i]) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+ }
+
+ cur_rcmap =
+ kmalloc(kpp_dev.kpp_rows * sizeof(cur_rcmap[0]), GFP_KERNEL);
+ prev_rcmap =
+ kmalloc(kpp_dev.kpp_rows * sizeof(prev_rcmap[0]), GFP_KERNEL);
+
+ if (!cur_rcmap || !prev_rcmap) {
+ retval = -ENOMEM;
+ goto err3;
+ }
+
+ __set_bit(EV_KEY, mxckbd_dev->evbit);
+
+ for (i = 0; i < mxckpd_keycodes_size; i++)
+ __set_bit(mxckpd_keycodes[i], mxckbd_dev->keybit);
+
+ for (i = 0; i < kpp_dev.kpp_rows; i++) {
+ memset(press_scancode[i], -1,
+ sizeof(press_scancode[0][0]) * kpp_dev.kpp_cols);
+ memset(release_scancode[i], -1,
+ sizeof(release_scancode[0][0]) * kpp_dev.kpp_cols);
+ }
+ memset(cur_rcmap, 0, kpp_dev.kpp_rows * sizeof(cur_rcmap[0]));
+ memset(prev_rcmap, 0, kpp_dev.kpp_rows * sizeof(prev_rcmap[0]));
+
+ key_pad_enabled = 1;
+ /* Initialize the polling timer */
+ init_timer(&kpp_dev.poll_timer);
+
+ /*
+ * Request for IRQ number for keypad port. The Interrupt handler
+ * function (mxc_kpp_interrupt) is called when ever interrupt occurs on
+ * keypad port.
+ */
+ retval = request_irq(irq, mxc_kpp_interrupt, 0, MOD_NAME, MOD_NAME);
+ if (retval) {
+ pr_debug("KPP: request_irq(%d) returned error %d\n",
+ MXC_INT_KPP, retval);
+ goto err3;
+ }
+
+ /* By default, devices should wakeup if they can */
+ /* So keypad is set as "should wakeup" as it can */
+ device_init_wakeup(&pdev->dev, 1);
+
+ return 0;
+
+ err3:
+ mxc_kpp_free_allocated();
+ err2:
+ input_free_device(mxckbd_dev);
+ err1:
+ free_irq(irq, MOD_NAME);
+ clk_disable(kpp_clk);
+ clk_put(kpp_clk);
+ return retval;
+}
+
+/*!
+ * Dissociates the driver from the kpp device.
+ *
+ * @param pdev the device structure used to give information on which SDHC
+ * to remove
+ *
+ * @return The function always returns 0.
+ */
+static int mxc_kpp_remove(struct platform_device *pdev)
+{
+ unsigned short reg_val;
+
+ /*
+ * Clear the KPKD status flag (write 1 to it) and synchronizer chain.
+ * Set KDIE control bit, clear KRIE control bit (avoid false release
+ * events. Disable the keypad GPIO pins.
+ */
+ __raw_writew(0x00, kpp_dev.base + KPCR);
+ __raw_writew(0x00, kpp_dev.base + KPDR);
+ __raw_writew(0x00, kpp_dev.base + KDDR);
+
+ reg_val = __raw_readw(kpp_dev.base + KPSR);
+ reg_val |= KBD_STAT_KPKD;
+ reg_val &= ~KBD_STAT_KRSS;
+ reg_val |= KBD_STAT_KDIE;
+ reg_val &= ~KBD_STAT_KRIE;
+ __raw_writew(reg_val, kpp_dev.base + KPSR);
+
+ gpio_keypad_inactive();
+ clk_disable(kpp_clk);
+ clk_put(kpp_clk);
+
+ KPress = 0;
+
+ del_timer(&kpp_dev.poll_timer);
+
+ free_irq(keypad->irq, MOD_NAME);
+ input_unregister_device(mxckbd_dev);
+
+ mxc_kpp_free_allocated();
+
+ return 0;
+}
+
+/*!
+ * This structure contains pointers to the power management callback functions.
+ */
+static struct platform_driver mxc_kpd_driver = {
+ .driver = {
+ .name = "mxc_keypad",
+ .bus = &platform_bus_type,
+ },
+ .suspend = mxc_kpp_suspend,
+ .resume = mxc_kpp_resume,
+ .probe = mxc_kpp_probe,
+ .remove = mxc_kpp_remove
+};
+
+/*!
+ * This function is called for module initialization.
+ * It registers keypad char driver and requests for KPP irq number. This
+ * function does the initialization of the keypad device.
+ *
+ * The following steps are used for keypad configuration,\n
+ * -# Enable number of rows in the keypad control register (KPCR[7:0}).\n
+ * -# Write 0's to KPDR[15:8]\n
+ * -# Configure keypad columns as open-drain (KPCR[15:8])\n
+ * -# Configure columns as output, rows as input (KDDR[15:0])\n
+ * -# Clear the KPKD status flag (write 1 to it) and synchronizer chain\n
+ * -# Set KDIE control bit, clear KRIE control bit\n
+ * In this function the keypad queue initialization is done.
+ * The keypad IOMUX configuration are done here.*
+
+ *
+ * @return 0 on success and a non-zero value on failure.
+ */
+static int __init mxc_kpp_init(void)
+{
+ printk(KERN_INFO "MXC keypad loaded\n");
+ platform_driver_register(&mxc_kpd_driver);
+ return 0;
+}
+
+/*!
+ * This function is called whenever the module is removed from the kernel. It
+ * unregisters the keypad driver from kernel and frees the irq number.
+ * This function puts the keypad to standby mode. The keypad interrupts are
+ * disabled. It calls gpio_keypad_inactive function to switch gpio
+ * configuration into default state.
+ *
+ */
+static void __exit mxc_kpp_cleanup(void)
+{
+ platform_driver_unregister(&mxc_kpd_driver);
+}
+
+module_init(mxc_kpp_init);
+module_exit(mxc_kpp_cleanup);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("MXC Keypad Controller Driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/mxs-kbd.c b/drivers/input/keyboard/mxs-kbd.c
new file mode 100644
index 000000000000..ed04a7eff19a
--- /dev/null
+++ b/drivers/input/keyboard/mxs-kbd.c
@@ -0,0 +1,364 @@
+/*
+ * Keypad ladder driver for Freescale MXS boards
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/io.h>
+#include <linux/ioport.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+
+#include <mach/device.h>
+#include <mach/hardware.h>
+#include <mach/regs-lradc.h>
+#include <mach/lradc.h>
+
+#define BUTTON_PRESS_THRESHOLD 3300
+#define LRADC_NOISE_MARGIN 100
+
+/* this value represents the the lradc value at 3.3V ( 3.3V / 0.000879 V/b ) */
+#define TARGET_VDDIO_LRADC_VALUE 3754
+
+struct mxskbd_data {
+ struct input_dev *input;
+ int last_button;
+ int irq;
+ int btn_irq;
+ struct mxskbd_keypair *keycodes;
+ unsigned int base;
+ int chan;
+ unsigned int btn_enable; /* detect enable bits */
+ unsigned int btn_irq_stat; /* detect irq status bits */
+ unsigned int btn_irq_ctrl; /* detect irq enable bits */
+};
+
+static int delay1 = 500;
+static int delay2 = 200;
+
+static int mxskbd_open(struct input_dev *dev);
+static void mxskbd_close(struct input_dev *dev);
+
+static struct mxskbd_data *mxskbd_data_alloc(struct platform_device *pdev,
+ struct mxskbd_keypair *keys)
+{
+ struct mxskbd_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
+
+ if (!d)
+ return NULL;
+
+ if (!keys) {
+ dev_err(&pdev->dev,
+ "No keycodes in platform_data, bailing out.\n");
+ kfree(d);
+ return NULL;
+ }
+ d->keycodes = keys;
+
+ d->input = input_allocate_device();
+ if (!d->input) {
+ kfree(d);
+ return NULL;
+ }
+
+ d->input->phys = "onboard";
+ d->input->uniq = "0000'0000";
+ d->input->name = pdev->name;
+ d->input->id.bustype = BUS_HOST;
+ d->input->open = mxskbd_open;
+ d->input->close = mxskbd_close;
+ d->input->dev.parent = &pdev->dev;
+
+ set_bit(EV_KEY, d->input->evbit);
+ set_bit(EV_REL, d->input->evbit);
+ set_bit(EV_REP, d->input->evbit);
+
+
+ d->last_button = -1;
+
+ while (keys->raw >= 0) {
+ set_bit(keys->kcode, d->input->keybit);
+ keys++;
+ }
+
+ return d;
+}
+
+static inline struct input_dev *GET_INPUT_DEV(struct mxskbd_data *d)
+{
+ BUG_ON(!d);
+ return d->input;
+}
+
+static void mxskbd_data_free(struct mxskbd_data *d)
+{
+ if (!d)
+ return;
+ if (d->input)
+ input_free_device(d->input);
+ kfree(d);
+}
+
+static unsigned mxskbd_decode_button(struct mxskbd_keypair *codes,
+ int raw_button)
+
+{
+ pr_debug("Decoding %d\n", raw_button);
+ while (codes->raw != -1) {
+ if ((raw_button >= (codes->raw - LRADC_NOISE_MARGIN)) &&
+ (raw_button < (codes->raw + LRADC_NOISE_MARGIN))) {
+ pr_debug("matches code 0x%x = %d\n",
+ codes->kcode, codes->kcode);
+ return codes->kcode;
+ }
+ codes++;
+ }
+ return (unsigned)-1; /* invalid key */
+}
+
+
+static irqreturn_t mxskbd_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct mxskbd_data *devdata = platform_get_drvdata(pdev);
+ u16 raw_button, normalized_button, vddio;
+ unsigned btn;
+
+ if (devdata->btn_irq == irq) {
+ __raw_writel(devdata->btn_irq_stat,
+ devdata->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << devdata->chan,
+ devdata->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << devdata->chan,
+ devdata->base + HW_LRADC_CTRL1_SET);
+ return IRQ_HANDLED;
+ }
+
+ raw_button = __raw_readl(devdata->base + HW_LRADC_CHn(devdata->chan)) &
+ BM_LRADC_CHn_VALUE;
+ vddio = hw_lradc_vddio();
+ BUG_ON(vddio == 0);
+
+ normalized_button = (raw_button * TARGET_VDDIO_LRADC_VALUE) /
+ vddio;
+
+ if (normalized_button < BUTTON_PRESS_THRESHOLD &&
+ devdata->last_button < 0) {
+ btn = mxskbd_decode_button(devdata->keycodes,
+ normalized_button);
+ if (btn < KEY_MAX) {
+ devdata->last_button = btn;
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, !0);
+ } else
+ dev_err(&pdev->dev, "Invalid button: raw = %d, "
+ "normalized = %d, vddio = %d\n",
+ raw_button, normalized_button, vddio);
+ } else if (devdata->last_button > 0 &&
+ normalized_button >= BUTTON_PRESS_THRESHOLD) {
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, 0);
+ devdata->last_button = -1;
+ if (devdata->btn_irq > 0)
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN <<
+ devdata->chan,
+ devdata->base + HW_LRADC_CTRL1_CLR);
+ }
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << devdata->chan,
+ devdata->base + HW_LRADC_CTRL1_CLR);
+ return IRQ_HANDLED;
+}
+
+static int mxskbd_open(struct input_dev *dev)
+{
+ /* enable clock */
+ return 0;
+}
+
+static void mxskbd_close(struct input_dev *dev)
+{
+ /* disable clock */
+}
+
+static void mxskbd_hwinit(struct platform_device *pdev)
+{
+ struct mxskbd_data *d = platform_get_drvdata(pdev);
+
+ hw_lradc_init_ladder(d->chan, LRADC_DELAY_TRIGGER_BUTTON, 200);
+ if (d->btn_irq > 0) {
+ __raw_writel(d->btn_enable, d->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(d->btn_irq_ctrl, d->base + HW_LRADC_CTRL1_SET);
+ } else {
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << d->chan,
+ d->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << d->chan,
+ d->base + HW_LRADC_CTRL1_SET);
+ }
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, !0);
+}
+
+#ifdef CONFIG_PM
+static int mxskbd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+ struct mxskbd_data *d = platform_get_drvdata(pdev);
+
+ hw_lradc_stop_ladder(d->chan, LRADC_DELAY_TRIGGER_BUTTON);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, 0);
+ hw_lradc_unuse_channel(d->chan);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << d->chan,
+ d->base + HW_LRADC_CTRL1_CLR);
+ mxskbd_close(d->input);
+ return 0;
+}
+
+static int mxskbd_resume(struct platform_device *pdev)
+{
+ struct mxskbd_data *d = platform_get_drvdata(pdev);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << d->chan,
+ d->base + HW_LRADC_CTRL1_SET);
+ mxskbd_open(d->input);
+ hw_lradc_use_channel(d->chan);
+ mxskbd_hwinit(pdev);
+ return 0;
+}
+#endif
+
+static int __devinit mxskbd_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ struct resource *res;
+ struct mxskbd_data *d;
+ struct mxs_kbd_plat_data *plat_data;
+
+ plat_data = (struct mxs_kbd_plat_data *)pdev->dev.platform_data;
+ if (plat_data == NULL)
+ return -ENODEV;
+
+ /* Create and register the input driver. */
+ d = mxskbd_data_alloc(pdev, plat_data->keypair);
+ if (!d) {
+ dev_err(&pdev->dev, "Cannot allocate driver structures\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ err = -ENODEV;
+ goto err_out;
+ }
+ d->base = (unsigned int)IO_ADDRESS(res->start);
+ d->chan = plat_data->channel;
+ d->irq = platform_get_irq(pdev, 0);
+ d->btn_irq = platform_get_irq(pdev, 1);
+ d->btn_enable = plat_data->btn_enable;
+ d->btn_irq_stat = plat_data->btn_irq_stat;
+ d->btn_irq_ctrl = plat_data->btn_irq_ctrl;
+
+ platform_set_drvdata(pdev, d);
+
+ err = request_irq(d->irq, mxskbd_irq_handler,
+ IRQF_DISABLED, pdev->name, pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot request keypad IRQ\n");
+ goto err_free_dev;
+ }
+
+ if (d->btn_irq > 0) {
+ err = request_irq(d->btn_irq, mxskbd_irq_handler,
+ IRQF_DISABLED, pdev->name, pdev);
+ if (err) {
+ dev_err(&pdev->dev,
+ "Cannot request keybad detect IRQ\n");
+ goto err_free_irq;
+ }
+ }
+
+ /* Register the input device */
+ err = input_register_device(GET_INPUT_DEV(d));
+ if (err)
+ goto err_free_irq2;
+
+ /* these two have to be set after registering the input device */
+ d->input->rep[REP_DELAY] = delay1;
+ d->input->rep[REP_PERIOD] = delay2;
+
+ hw_lradc_use_channel(d->chan);
+ mxskbd_hwinit(pdev);
+
+ return 0;
+
+err_free_irq2:
+ platform_set_drvdata(pdev, NULL);
+ if (d->btn_irq > 0)
+ free_irq(d->btn_irq, pdev);
+err_free_irq:
+ free_irq(d->irq, pdev);
+err_free_dev:
+ mxskbd_data_free(d);
+err_out:
+ return err;
+}
+
+static int __devexit mxskbd_remove(struct platform_device *pdev)
+{
+ struct mxskbd_data *d = platform_get_drvdata(pdev);
+
+ hw_lradc_unuse_channel(d->chan);
+ input_unregister_device(GET_INPUT_DEV(d));
+ free_irq(d->irq, pdev);
+ if (d->btn_irq > 0)
+ free_irq(d->btn_irq, pdev);
+ mxskbd_data_free(d);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver mxskbd_driver = {
+ .probe = mxskbd_probe,
+ .remove = __devexit_p(mxskbd_remove),
+#ifdef CONFIG_PM
+ .suspend = mxskbd_suspend,
+ .resume = mxskbd_resume,
+#endif
+ .driver = {
+ .name = "mxs-kbd",
+ },
+};
+
+static int __init mxskbd_init(void)
+{
+ return platform_driver_register(&mxskbd_driver);
+}
+
+static void __exit mxskbd_exit(void)
+{
+ platform_driver_unregister(&mxskbd_driver);
+}
+
+module_init(mxskbd_init);
+module_exit(mxskbd_exit);
+MODULE_DESCRIPTION("Freescale keyboard driver for mxs family");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>")
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/keyboard/stmp3xxx-kbd.c b/drivers/input/keyboard/stmp3xxx-kbd.c
new file mode 100644
index 000000000000..a230aa92710c
--- /dev/null
+++ b/drivers/input/keyboard/stmp3xxx-kbd.c
@@ -0,0 +1,307 @@
+/*
+ * Keypad ladder driver for Freescale STMP37XX/STMP378X boards
+ *
+ * Author: dmitry pervushin <dimka@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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
+ */
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <mach/regs-lradc.h>
+#include <mach/lradc.h>
+#include <mach/stmp3xxx.h>
+#include <mach/platform.h>
+
+#define BUTTON_PRESS_THRESHOLD 3300
+#define LRADC_NOISE_MARGIN 100
+
+/* this value represents the the lradc value at 3.3V ( 3.3V / 0.000879 V/b ) */
+#define TARGET_VDDIO_LRADC_VALUE 3754
+
+struct stmpkbd_data {
+ struct input_dev *input;
+ int last_button;
+ int irq;
+ struct stmpkbd_keypair *keycodes;
+};
+
+static int delay1 = 500;
+static int delay2 = 200;
+
+static int stmpkbd_open(struct input_dev *dev);
+static void stmpkbd_close(struct input_dev *dev);
+
+static struct stmpkbd_data *stmpkbd_data_alloc(struct platform_device *pdev,
+ struct stmpkbd_keypair *keys)
+{
+ struct stmpkbd_data *d = kzalloc(sizeof(*d), GFP_KERNEL);
+
+ if (!d)
+ return NULL;
+
+ if (!keys) {
+ dev_err(&pdev->dev,
+ "No keycodes in platform_data, bailing out.\n");
+ kfree(d);
+ return NULL;
+ }
+ d->keycodes = keys;
+
+ d->input = input_allocate_device();
+ if (!d->input) {
+ kfree(d);
+ return NULL;
+ }
+
+ d->input->phys = "onboard";
+ d->input->uniq = "0000'0000";
+ d->input->name = pdev->name;
+ d->input->id.bustype = BUS_HOST;
+ d->input->open = stmpkbd_open;
+ d->input->close = stmpkbd_close;
+ d->input->dev.parent = &pdev->dev;
+
+ set_bit(EV_KEY, d->input->evbit);
+ set_bit(EV_REL, d->input->evbit);
+ set_bit(EV_REP, d->input->evbit);
+
+
+ d->last_button = -1;
+
+ while (keys->raw >= 0) {
+ set_bit(keys->kcode, d->input->keybit);
+ keys++;
+ }
+
+ return d;
+}
+
+static inline struct input_dev *GET_INPUT_DEV(struct stmpkbd_data *d)
+{
+ BUG_ON(!d);
+ return d->input;
+}
+
+static void stmpkbd_data_free(struct stmpkbd_data *d)
+{
+ if (!d)
+ return;
+ if (d->input)
+ input_free_device(d->input);
+ kfree(d);
+}
+
+static unsigned stmpkbd_decode_button(struct stmpkbd_keypair *codes,
+ int raw_button)
+
+{
+ pr_debug("Decoding %d\n", raw_button);
+ while (codes->raw != -1) {
+ if ((raw_button > (codes->raw - LRADC_NOISE_MARGIN)) &&
+ (raw_button < (codes->raw + LRADC_NOISE_MARGIN))) {
+ pr_debug("matches code 0x%x = %d\n",
+ codes->kcode, codes->kcode);
+ return codes->kcode;
+ }
+ codes++;
+ }
+ return (unsigned)-1; /* invalid key */
+}
+
+
+static irqreturn_t stmpkbd_irq_handler(int irq, void *dev_id)
+{
+ struct platform_device *pdev = dev_id;
+ struct stmpkbd_data *devdata = platform_get_drvdata(pdev);
+ u16 raw_button, normalized_button, vddio;
+ unsigned btn;
+
+ raw_button = __raw_readl(REGS_LRADC_BASE +
+ HW_LRADC_CHn(LRADC_CH0)) & BM_LRADC_CHn_VALUE;
+ vddio = hw_lradc_vddio();
+ BUG_ON(vddio == 0);
+
+ normalized_button = (raw_button * TARGET_VDDIO_LRADC_VALUE) /
+ vddio;
+
+ if (normalized_button < BUTTON_PRESS_THRESHOLD &&
+ devdata->last_button < 0) {
+
+ btn = stmpkbd_decode_button(devdata->keycodes,
+ normalized_button);
+
+ if (btn < KEY_MAX) {
+ devdata->last_button = btn;
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, !0);
+ } else
+ dev_err(&pdev->dev, "Invalid button: raw = %d, "
+ "normalized = %d, vddio = %d\n",
+ raw_button, normalized_button, vddio);
+ } else if (devdata->last_button > 0 &&
+ normalized_button >= BUTTON_PRESS_THRESHOLD) {
+
+ input_report_key(GET_INPUT_DEV(devdata),
+ devdata->last_button, 0);
+ devdata->last_button = -1;
+
+ }
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ return IRQ_HANDLED;
+}
+
+static int stmpkbd_open(struct input_dev *dev)
+{
+ /* enable clock */
+ return 0;
+}
+
+static void stmpkbd_close(struct input_dev *dev)
+{
+ /* disable clock */
+}
+
+static void stmpkbd_hwinit(struct platform_device *pdev)
+{
+ hw_lradc_init_ladder(LRADC_CH0, LRADC_DELAY_TRIGGER_BUTTON, 200);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, !0);
+}
+
+static int stmpkbd_suspend(struct platform_device *pdev, pm_message_t state)
+{
+#ifdef CONFIG_PM
+ struct input_dev *idev = platform_get_drvdata(pdev);
+
+ hw_lradc_stop_ladder(LRADC_CH0, LRADC_DELAY_TRIGGER_BUTTON);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_BUTTON, 0);
+ hw_lradc_unuse_channel(LRADC_CH0);
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ stmpkbd_close(idev);
+#endif
+ return 0;
+}
+
+static int stmpkbd_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+ struct input_dev *idev = platform_get_drvdata(pdev);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+ stmpkbd_open(idev);
+ hw_lradc_use_channel(LRADC_CH0);
+ stmpkbd_hwinit(pdev);
+#endif
+ return 0;
+}
+
+static int __devinit stmpkbd_probe(struct platform_device *pdev)
+{
+ int err = 0;
+ int irq = platform_get_irq(pdev, 0);
+ struct stmpkbd_data *d;
+
+ /* Create and register the input driver. */
+ d = stmpkbd_data_alloc(pdev,
+ (struct stmpkbd_keypair *)pdev->dev.platform_data);
+ if (!d) {
+ dev_err(&pdev->dev, "Cannot allocate driver structures\n");
+ err = -ENOMEM;
+ goto err_out;
+ }
+
+ d->irq = irq;
+ err = request_irq(irq, stmpkbd_irq_handler,
+ IRQF_DISABLED, pdev->name, pdev);
+ if (err) {
+ dev_err(&pdev->dev, "Cannot request keypad IRQ\n");
+ goto err_free_dev;
+ }
+
+ platform_set_drvdata(pdev, d);
+
+ /* Register the input device */
+ err = input_register_device(GET_INPUT_DEV(d));
+ if (err)
+ goto err_free_irq;
+
+ /* these two have to be set after registering the input device */
+ d->input->rep[REP_DELAY] = delay1;
+ d->input->rep[REP_PERIOD] = delay2;
+
+ hw_lradc_use_channel(LRADC_CH0);
+ stmpkbd_hwinit(pdev);
+
+ return 0;
+
+err_free_irq:
+ platform_set_drvdata(pdev, NULL);
+ free_irq(irq, pdev);
+err_free_dev:
+ stmpkbd_data_free(d);
+err_out:
+ return err;
+}
+
+static int __devexit stmpkbd_remove(struct platform_device *pdev)
+{
+ struct stmpkbd_data *d = platform_get_drvdata(pdev);
+
+ hw_lradc_unuse_channel(LRADC_CH0);
+ input_unregister_device(GET_INPUT_DEV(d));
+ free_irq(d->irq, pdev);
+ stmpkbd_data_free(d);
+
+ platform_set_drvdata(pdev, NULL);
+
+ return 0;
+}
+
+static struct platform_driver stmpkbd_driver = {
+ .probe = stmpkbd_probe,
+ .remove = __devexit_p(stmpkbd_remove),
+ .suspend = stmpkbd_suspend,
+ .resume = stmpkbd_resume,
+ .driver = {
+ .name = "stmp3xxx-keyboard",
+ },
+};
+
+static int __init stmpkbd_init(void)
+{
+ return platform_driver_register(&stmpkbd_driver);
+}
+
+static void __exit stmpkbd_exit(void)
+{
+ platform_driver_unregister(&stmpkbd_driver);
+}
+
+module_init(stmpkbd_init);
+module_exit(stmpkbd_exit);
+MODULE_DESCRIPTION("Freescale STMP3xxxx keyboard driver");
+MODULE_AUTHOR("dmitry pervushin <dimka@embeddedalley.com>");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig
index 1acfa3a05aad..afee839ba338 100644
--- a/drivers/input/misc/Kconfig
+++ b/drivers/input/misc/Kconfig
@@ -40,6 +40,15 @@ config INPUT_M68K_BEEP
tristate "M68k Beeper support"
depends on M68K
+config INPUT_STMP3XXX_ROTDEC
+ tristate "STMP3xxx Rotary Decoder support"
+ depends on MACH_STMP378X
+ select INPUT_POLLDEV
+ help
+ Say Y here for support for the rotary decoder on STMP3xxx
+
+ If compiled as a module, it will be called stmp3xxx_rotdec
+
config INPUT_APANEL
tristate "Fujitsu Lifebook Application Panel buttons"
depends on X86 && I2C && LEDS_CLASS
diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile
index 0d979fd4cd57..0cb5e795cfb4 100644
--- a/drivers/input/misc/Makefile
+++ b/drivers/input/misc/Makefile
@@ -26,3 +26,4 @@ obj-$(CONFIG_INPUT_TWL4030_PWRBUTTON) += twl4030-pwrbutton.o
obj-$(CONFIG_INPUT_UINPUT) += uinput.o
obj-$(CONFIG_INPUT_WISTRON_BTNS) += wistron_btns.o
obj-$(CONFIG_INPUT_YEALINK) += yealink.o
+obj-$(CONFIG_INPUT_STMP3XXX_ROTDEC) += stmp3xxx_rotdec.o
diff --git a/drivers/input/misc/stmp3xxx_rotdec.c b/drivers/input/misc/stmp3xxx_rotdec.c
new file mode 100644
index 000000000000..621db95dba8c
--- /dev/null
+++ b/drivers/input/misc/stmp3xxx_rotdec.c
@@ -0,0 +1,174 @@
+/*
+ * Freescale STMP3XXX Rotary Encoder Driver
+ *
+ * Author: Drew Benedetti <drewb@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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
+ */
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/platform_device.h>
+#include <linux/input-polldev.h>
+#include <mach/regs-timrot.h>
+#include <mach/rotdec.h>
+#include <mach/platform.h>
+
+static int relative;
+static unsigned int poll_interval = 500;
+
+void stmp3xxx_rotdec_flush(struct input_polled_dev *dev)
+{
+ /* in relative mode, reading the counter resets it */
+ if (relative)
+ __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCOUNT);
+}
+
+void stmp3xxx_rotdec_poll(struct input_polled_dev *dev)
+{
+ s16 cnt = __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCOUNT) & BM_TIMROT_ROTCOUNT_UPDOWN;
+ if (relative)
+ input_report_rel(dev->input, REL_WHEEL, cnt);
+ else
+ input_report_abs(dev->input, ABS_WHEEL, cnt);
+}
+
+struct input_polled_dev *rotdec;
+static u32 rotctrl;
+
+static int stmp3xxx_rotdec_probe(struct platform_device *pdev)
+{
+ int rc = 0;
+
+ /* save original state of HW_TIMROT_ROTCTRL */
+ rotctrl = __raw_readl(REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL);
+
+ if (!(rotctrl & BM_TIMROT_ROTCTRL_ROTARY_PRESENT)) {
+ dev_info(&pdev->dev, "No rotary decoder present\n");
+ rc = -ENODEV;
+ goto err_rotdec_present;
+ } else {
+ /* I had to add some extra line breaks in here
+ * to avoid lines >80 chars wide
+ */
+ __raw_writel(
+ BF(0x0, TIMROT_ROTCTRL_DIVIDER) | /* 32kHz divider - 1 */
+ BF(BV_TIMROT_ROTCTRL_OVERSAMPLE__2X,
+ TIMROT_ROTCTRL_OVERSAMPLE) |
+ BF(BV_TIMROT_ROTCTRL_SELECT_B__ROTARYB,
+ TIMROT_ROTCTRL_SELECT_B) |
+ BF(BV_TIMROT_ROTCTRL_SELECT_A__ROTARYA,
+ TIMROT_ROTCTRL_SELECT_A)
+ , REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL);
+ __raw_writel(
+ BM_TIMROT_ROTCTRL_POLARITY_B |
+ BM_TIMROT_ROTCTRL_POLARITY_A
+ , REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_CLR);
+
+ if (relative)
+ __raw_writel(BM_TIMROT_ROTCTRL_RELATIVE,
+ REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_SET);
+ else
+ __raw_writel(BM_TIMROT_ROTCTRL_RELATIVE,
+ REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL_CLR);
+
+ rc = rotdec_pinmux_request();
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Pin request failed (err=%d)\n", rc);
+ goto err_pinmux;
+ }
+
+ /* set up input_polled_dev */
+ rotdec = input_allocate_polled_device();
+ if (!rotdec) {
+ dev_err(&pdev->dev,
+ "Unable to allocate polled device\n");
+ rc = -ENOMEM;
+ goto err_alloc_polldev;
+ }
+ rotdec->flush = stmp3xxx_rotdec_flush;
+ rotdec->poll = stmp3xxx_rotdec_poll;
+ rotdec->poll_interval = poll_interval; /* msec */
+
+ rotdec->input->name = "stmp3xxx-rotdec";
+ if (relative)
+ input_set_capability(rotdec->input, EV_REL, REL_WHEEL);
+ else {
+ input_set_capability(rotdec->input, EV_ABS, ABS_WHEEL);
+ input_set_abs_params(rotdec->input, ABS_WHEEL,
+ -32768, 32767, 0, 0);
+ }
+
+ rc = input_register_polled_device(rotdec);
+ if (rc) {
+ dev_err(&pdev->dev,
+ "Unable to register rotary decoder (err=%d)\n",
+ rc);
+ goto err_reg_polldev;
+ }
+ }
+
+ return 0;
+
+err_reg_polldev:
+ input_free_polled_device(rotdec);
+err_alloc_polldev:
+ rotdec_pinmux_free();
+err_pinmux:
+ /* restore original register state */
+ __raw_writel(rotctrl, REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL);
+
+err_rotdec_present:
+ return rc;
+}
+
+static int stmp3xxx_rotdec_remove(struct platform_device *pdev)
+{
+ input_unregister_polled_device(rotdec);
+ input_free_polled_device(rotdec);
+
+ rotdec_pinmux_free();
+
+ /* restore original register state */
+ __raw_writel(rotctrl, REGS_TIMROT_BASE + HW_TIMROT_ROTCTRL);
+
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_rotdec_driver = {
+ .probe = stmp3xxx_rotdec_probe,
+ .remove = stmp3xxx_rotdec_remove,
+ .driver = {
+ .name = "stmp3xxx-rotdec",
+ },
+};
+
+static int __init stmp3xxx_rotdec_init(void)
+{
+ return platform_driver_register(&stmp3xxx_rotdec_driver);
+}
+
+static void __exit stmp3xxx_rotdec_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_rotdec_driver);
+}
+
+module_init(stmp3xxx_rotdec_init);
+module_exit(stmp3xxx_rotdec_exit);
+
+module_param(relative, bool, 0600);
+module_param(poll_interval, uint, 0600);
+
+MODULE_AUTHOR("Drew Benedetti <drewb@embeddedalley.com>");
+MODULE_DESCRIPTION("STMP3xxx rotary decoder driver");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/Kconfig b/drivers/input/touchscreen/Kconfig
index 72e2712c7e2a..cd489febde11 100644
--- a/drivers/input/touchscreen/Kconfig
+++ b/drivers/input/touchscreen/Kconfig
@@ -225,6 +225,46 @@ config TOUCHSCREEN_HP7XX
To compile this driver as a module, choose M here: the
module will be called jornada720_ts.
+config TOUCHSCREEN_MXC
+ tristate "MXC touchscreen input driver"
+ depends on MXC_MC13783_ADC || MXC_MC13892_ADC
+ help
+ Say Y here if you have an MXC based board with touchscreen
+ attached to it.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called mxc_ts.
+
+config TOUCHSCREEN_IMX_ADC
+ tristate "Freescale i.MX ADC touchscreen input driver"
+ depends on IMX_ADC
+ help
+ Say Y here if you have a Freescale i.MX based board with a
+ touchscreen interfaced to the processor's integrated ADC.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here: the
+ module will be called imx_adc_ts.
+
+config TOUCHSCREEN_STMP3XXX
+ tristate "STMP3XXX LRADC-based touchscreen"
+ depends on ARCH_STMP3XXX
+ select SERIO
+ help
+ Say Y here if you want to enable TMP3XXX LRADC-based touchscreen.
+ module will be called stmp3xxx_ts.
+
+config TOUCHSCREEN_MXS
+ tristate "MXS LRADC-based touchscreen"
+ depends on ARCH_MXS
+ select SERIO
+ help
+ Say Y here if you want to enable MXS LRADC-based touchscreen.
+ module will be called mxs-ts.
+
config TOUCHSCREEN_HTCPEN
tristate "HTC Shift X9500 touchscreen"
depends on ISA
diff --git a/drivers/input/touchscreen/Makefile b/drivers/input/touchscreen/Makefile
index 3e1c5e0b952f..05589702fdaa 100644
--- a/drivers/input/touchscreen/Makefile
+++ b/drivers/input/touchscreen/Makefile
@@ -23,6 +23,10 @@ obj-$(CONFIG_TOUCHSCREEN_MK712) += mk712.o
obj-$(CONFIG_TOUCHSCREEN_HP600) += hp680_ts_input.o
obj-$(CONFIG_TOUCHSCREEN_HP7XX) += jornada720_ts.o
obj-$(CONFIG_TOUCHSCREEN_HTCPEN) += htcpen.o
+obj-$(CONFIG_TOUCHSCREEN_MXC) += mxc_ts.o
+obj-$(CONFIG_TOUCHSCREEN_IMX_ADC) += imx_adc_ts.o
+obj-$(CONFIG_TOUCHSCREEN_STMP3XXX) += stmp3xxx_ts.o
+obj-$(CONFIG_TOUCHSCREEN_MXS) += mxs-ts.o
obj-$(CONFIG_TOUCHSCREEN_USB_COMPOSITE) += usbtouchscreen.o
obj-$(CONFIG_TOUCHSCREEN_PENMOUNT) += penmount.o
obj-$(CONFIG_TOUCHSCREEN_TOUCHIT213) += touchit213.o
diff --git a/drivers/input/touchscreen/imx_adc_ts.c b/drivers/input/touchscreen/imx_adc_ts.c
new file mode 100644
index 000000000000..ec43a16213ae
--- /dev/null
+++ b/drivers/input/touchscreen/imx_adc_ts.c
@@ -0,0 +1,114 @@
+/*
+ * Copyright 2009 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 imx_adc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor i.MX ADC touchscreen.
+ *
+ * This touchscreen driver is designed as a standard input driver. It is a
+ * wrapper around the low level ADC driver. Much of the hardware configuration
+ * and touchscreen functionality is implemented in the low level ADC driver.
+ * During initialization, this driver creates a kernel thread. This thread
+ * then calls the ADC driver to obtain touchscreen values continously. These
+ * values are then passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/kthread.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/imx_adc.h>
+
+#define IMX_ADC_TS_NAME "imx_adc_ts"
+
+static struct input_dev *imx_inputdev;
+static u32 input_ts_installed;
+
+static int ts_thread(void *arg)
+{
+ struct t_touch_screen ts_sample;
+ int wait = 0;
+ daemonize("imx_adc_ts");
+ while (input_ts_installed) {
+ try_to_freeze();
+
+ memset(&ts_sample, 0, sizeof(ts_sample));
+ if (0 != imx_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+
+ input_report_abs(imx_inputdev, ABS_X, ts_sample.x_position);
+ input_report_abs(imx_inputdev, ABS_Y, ts_sample.y_position);
+ input_report_abs(imx_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+ input_sync(imx_inputdev);
+ wait = ts_sample.contact_resistance;
+ msleep(10);
+ }
+
+ return 0;
+}
+
+static int __init imx_adc_ts_init(void)
+{
+ int retval;
+
+ if (!is_imx_adc_ready())
+ return -ENODEV;
+
+ imx_inputdev = input_allocate_device();
+ if (!imx_inputdev) {
+ pr_err("imx_ts_init: not enough memory for input device\n");
+ return -ENOMEM;
+ }
+
+ imx_inputdev->name = IMX_ADC_TS_NAME;
+ imx_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ imx_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ imx_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(imx_inputdev);
+ if (retval < 0) {
+ input_free_device(imx_inputdev);
+ return retval;
+ }
+
+ input_ts_installed = 1;
+ kthread_run(ts_thread, NULL, "ts_thread");
+ pr_info("i.MX ADC input touchscreen loaded.\n");
+ return 0;
+}
+
+static void __exit imx_adc_ts_exit(void)
+{
+ input_ts_installed = 0;
+ input_unregister_device(imx_inputdev);
+
+ if (imx_inputdev) {
+ input_free_device(imx_inputdev);
+ imx_inputdev = NULL;
+ }
+}
+
+late_initcall(imx_adc_ts_init);
+module_exit(imx_adc_ts_exit);
+
+MODULE_DESCRIPTION("i.MX ADC input touchscreen driver");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mxc_ts.c b/drivers/input/touchscreen/mxc_ts.c
new file mode 100644
index 000000000000..0783188e8376
--- /dev/null
+++ b/drivers/input/touchscreen/mxc_ts.c
@@ -0,0 +1,189 @@
+/*
+ * Freescale touchscreen driver
+ *
+ * Copyright (C) 2007-2010 Freescale Semiconductor, Inc. All Rights Reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/*!
+ * @file mxc_ts.c
+ *
+ * @brief Driver for the Freescale Semiconductor MXC touchscreen with calibration support.
+ *
+ * The touchscreen driver is designed as a standard input driver which is a
+ * wrapper over low level PMIC driver. Most of the hardware configuration and
+ * touchscreen functionality is implemented in the low level PMIC driver. During
+ * initialization, this driver creates a kernel thread. This thread then calls
+ * PMIC driver to obtain touchscreen values continously. These values are then
+ * passed to the input susbsystem.
+ *
+ * @ingroup touchscreen
+ */
+
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/input.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/freezer.h>
+#include <linux/pmic_external.h>
+#include <linux/pmic_adc.h>
+#include <linux/kthread.h>
+
+#define MXC_TS_NAME "mxc_ts"
+
+static struct input_dev *mxc_inputdev;
+static struct task_struct *tstask;
+/**
+ * calibration array refers to
+ * (delta_x[0], delta_x[1], delta_x[2], delta_y[0], delta_y[1], delta_y[2], delta).
+ * Which generated by calibration service.
+ * In this driver when we got touch pointer (x', y') from PMIC ADC,
+ * we calculate the display pointer (x,y) by:
+ * x = (delta_x[0] * x' + delta_x[1] * y' + delta_x[2]) / delta;
+ * y = (delta_y[0] * x' + delta_y[1] * y' + delta_y[2]) / delta;
+ */
+static int calibration[7];
+module_param_array(calibration, int, NULL, S_IRUGO | S_IWUSR);
+
+static int ts_thread(void *arg)
+{
+ t_touch_screen ts_sample;
+ s32 wait = 0;
+
+ do {
+ int x, y;
+ static int last_x = -1, last_y = -1, last_press = -1;
+
+ memset(&ts_sample, 0, sizeof(t_touch_screen));
+ if (0 != pmic_adc_get_touch_sample(&ts_sample, !wait))
+ continue;
+ if (!(ts_sample.contact_resistance || wait))
+ continue;
+
+ if (ts_sample.x_position == 0 && ts_sample.y_position == 0 &&
+ ts_sample.contact_resistance == 0) {
+ x = last_x;
+ y = last_y;
+ } else if (calibration[6] == 0) {
+ x = ts_sample.x_position;
+ y = ts_sample.y_position;
+ } else {
+ x = calibration[0] * (int)ts_sample.x_position +
+ calibration[1] * (int)ts_sample.y_position +
+ calibration[2];
+ x /= calibration[6];
+ if (x < 0)
+ x = 0;
+ y = calibration[3] * (int)ts_sample.x_position +
+ calibration[4] * (int)ts_sample.y_position +
+ calibration[5];
+ y /= calibration[6];
+ if (y < 0)
+ y = 0;
+ }
+
+ if (x != last_x) {
+ input_report_abs(mxc_inputdev, ABS_X, x);
+ last_x = x;
+ }
+ if (y != last_y) {
+ input_report_abs(mxc_inputdev, ABS_Y, y);
+ last_y = y;
+ }
+
+ /* report pressure */
+ input_report_abs(mxc_inputdev, ABS_PRESSURE,
+ ts_sample.contact_resistance);
+#ifdef CONFIG_MXC_PMIC_MC13892
+ /* workaround for aplite ADC resistance large range value */
+ if (ts_sample.contact_resistance > 22)
+ ts_sample.contact_resistance = 1;
+ else
+ ts_sample.contact_resistance = 0;
+#endif
+ /* report the BTN_TOUCH */
+ if (ts_sample.contact_resistance != last_press)
+ input_event(mxc_inputdev, EV_KEY,
+ BTN_TOUCH, ts_sample.contact_resistance);
+
+ input_sync(mxc_inputdev);
+ last_press = ts_sample.contact_resistance;
+
+ wait = ts_sample.contact_resistance;
+ msleep(20);
+
+ } while (!kthread_should_stop());
+
+ return 0;
+}
+
+static int __init mxc_ts_init(void)
+{
+ int retval;
+
+ if (!is_pmic_adc_ready())
+ return -ENODEV;
+
+ mxc_inputdev = input_allocate_device();
+ if (!mxc_inputdev) {
+ printk(KERN_ERR
+ "mxc_ts_init: not enough memory\n");
+ return -ENOMEM;
+ }
+
+ mxc_inputdev->name = MXC_TS_NAME;
+ mxc_inputdev->evbit[0] = BIT_MASK(EV_KEY) | BIT_MASK(EV_ABS);
+ mxc_inputdev->keybit[BIT_WORD(BTN_TOUCH)] |= BIT_MASK(BTN_TOUCH);
+ mxc_inputdev->absbit[0] =
+ BIT_MASK(ABS_X) | BIT_MASK(ABS_Y) | BIT_MASK(ABS_PRESSURE);
+ retval = input_register_device(mxc_inputdev);
+ if (retval < 0) {
+ input_free_device(mxc_inputdev);
+ return retval;
+ }
+
+ tstask = kthread_run(ts_thread, NULL, "mxc_ts");
+ if (IS_ERR(tstask)) {
+ printk(KERN_ERR
+ "mxc_ts_init: failed to create kthread");
+ tstask = NULL;
+ return -1;
+ }
+ printk("mxc input touchscreen loaded\n");
+ return 0;
+}
+
+static void __exit mxc_ts_exit(void)
+{
+ if (tstask)
+ kthread_stop(tstask);
+
+ input_unregister_device(mxc_inputdev);
+
+ if (mxc_inputdev) {
+ input_free_device(mxc_inputdev);
+ mxc_inputdev = NULL;
+ }
+}
+
+late_initcall(mxc_ts_init);
+module_exit(mxc_ts_exit);
+
+MODULE_DESCRIPTION("MXC touchscreen driver with calibration");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_LICENSE("GPL");
diff --git a/drivers/input/touchscreen/mxs-ts.c b/drivers/input/touchscreen/mxs-ts.c
new file mode 100644
index 000000000000..a2ea102290a8
--- /dev/null
+++ b/drivers/input/touchscreen/mxs-ts.c
@@ -0,0 +1,462 @@
+/*
+ * Freesclae MXS Touchscreen driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2010 Freescale Semiconductor, Inc.
+ * Copyright 2008 Embedded Alley Solutions, 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
+ */
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <mach/lradc.h>
+#include <mach/hardware.h>
+#include <mach/device.h>
+#include <mach/regs-lradc.h>
+
+#define TOUCH_DEBOUNCE_TOLERANCE 100
+
+struct mxs_ts_info {
+ int touch_irq;
+ int device_irq;
+ unsigned int base;
+ u8 x_plus_chan;
+ u8 x_minus_chan;
+ u8 y_plus_chan;
+ u8 y_minus_chan;
+
+ unsigned int x_plus_val;
+ unsigned int x_minus_val;
+ unsigned int y_plus_val;
+ unsigned int y_minus_val;
+ unsigned int x_plus_mask;
+ unsigned int x_minus_mask;
+ unsigned int y_plus_mask;
+ unsigned int y_minus_mask;
+
+ struct input_dev *idev;
+ enum {
+ TS_STATE_DISABLED,
+ TS_STATE_TOUCH_DETECT,
+ TS_STATE_TOUCH_VERIFY,
+ TS_STATE_X_PLANE,
+ TS_STATE_Y_PLANE,
+ } state;
+ u16 x;
+ u16 y;
+ int sample_count;
+};
+
+static inline void enter_state_touch_detect(struct mxs_ts_info *info)
+{
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+ /*
+ * turn off the yplus and yminus pullup and pulldown, and turn off touch
+ * detect (enables yminus, and xplus through a resistor.On a press,
+ * xplus is pulled down)
+ */
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_SET);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_TOUCH_DETECT;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_disabled(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_DISABLED;
+ info->sample_count = 0;
+}
+
+
+static inline void enter_state_x_plane(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->y_minus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+
+ info->state = TS_STATE_X_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_y_plane(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(info->x_minus_val, info->base + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_CLR);
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->state = TS_STATE_Y_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_touch_verify(struct mxs_ts_info *info)
+{
+ __raw_writel(info->y_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->y_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_plus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(info->x_minus_mask, info->base + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ info->base + HW_LRADC_CTRL0_SET);
+ info->state = TS_STATE_TOUCH_VERIFY;
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->sample_count = 0;
+}
+
+static void process_lradc(struct mxs_ts_info *info, u16 x, u16 y,
+ int pressure)
+{
+ switch (info->state) {
+ case TS_STATE_X_PLANE:
+ pr_debug("%s: x plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->x = x;
+ info->sample_count++;
+ } else {
+ if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 x_c = info->x * (info->sample_count - 1);
+ info->x = (x_c + x) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_y_plane(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_Y_PLANE:
+ pr_debug("%s: y plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->y = y;
+ info->sample_count++;
+ } else {
+ if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 y_c = info->y * (info->sample_count - 1);
+ info->y = (y_c + y) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_touch_verify(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_TOUCH_VERIFY:
+ pr_debug("%s: touch verify state, sample_count %d\n", __func__,
+ info->sample_count);
+ pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
+ input_report_abs(info->idev, ABS_X, info->x);
+ input_report_abs(info->idev, ABS_Y, info->y);
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ input_sync(info->idev);
+ /* fall through */
+ case TS_STATE_TOUCH_DETECT:
+ pr_debug("%s: touch detect state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (pressure) {
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ enter_state_x_plane(info);
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ } else
+ enter_state_touch_detect(info);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
+ info->state);
+ }
+}
+
+static irqreturn_t ts_handler(int irq, void *dev_id)
+{
+ struct mxs_ts_info *info = dev_id;
+ u16 x_plus, y_plus;
+ int pressure = 0;
+
+ if (irq == info->touch_irq)
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+ else if (irq == info->device_irq)
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ /* get x, y values */
+ x_plus = __raw_readl(info->base + HW_LRADC_CHn(info->x_plus_chan)) &
+ BM_LRADC_CHn_VALUE;
+ y_plus = __raw_readl(info->base + HW_LRADC_CHn(info->y_plus_chan)) &
+ BM_LRADC_CHn_VALUE;
+
+ /* pressed? */
+ if (__raw_readl(info->base + HW_LRADC_STATUS) &
+ BM_LRADC_STATUS_TOUCH_DETECT_RAW)
+ pressure = 1;
+
+ pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
+ __func__, irq, x_plus, y_plus, pressure);
+
+ process_lradc(info, x_plus, y_plus, pressure);
+
+ return IRQ_HANDLED;
+}
+
+static int __devinit mxs_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *idev;
+ struct mxs_ts_info *info;
+ int ret = 0;
+ struct resource *res;
+ struct mxs_touchscreen_plat_data *plat_data;
+
+ plat_data = (struct mxs_touchscreen_plat_data *)pdev->dev.platform_data;
+ if (plat_data == NULL)
+ return -ENODEV;
+
+ idev = input_allocate_device();
+ if (idev == NULL)
+ return -ENOMEM;
+
+ info = kzalloc(sizeof(struct mxs_ts_info), GFP_KERNEL);
+ if (info == NULL) {
+ ret = -ENOMEM;
+ goto out_nomem_info;
+ }
+
+ idev->name = "MXS touchscreen";
+ idev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret)
+ goto out_nomem;
+
+ info->idev = idev;
+ info->x_plus_chan = plat_data->x_plus_chan;
+ info->x_minus_chan = plat_data->x_minus_chan;
+ info->y_plus_chan = plat_data->y_plus_chan;
+ info->y_minus_chan = plat_data->y_minus_chan;
+ info->x_plus_val = plat_data->x_plus_val;
+ info->x_minus_val = plat_data->x_minus_val;
+ info->y_plus_val = plat_data->y_plus_val;
+ info->y_minus_val = plat_data->y_minus_val;
+ info->x_plus_mask = plat_data->x_plus_mask;
+ info->x_minus_mask = plat_data->x_minus_mask;
+ info->y_plus_mask = plat_data->y_plus_mask;
+ info->y_minus_mask = plat_data->y_minus_mask;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get MEM resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->base = (unsigned int)IO_ADDRESS(res->start);
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->touch_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->device_irq = res->start;
+
+ ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
+ "mxs_ts_touch", info);
+ if (ret)
+ goto out_nodev;
+
+ ret = request_irq(info->device_irq, ts_handler, IRQF_DISABLED,
+ "mxs_ts_dev", info);
+ if (ret) {
+ free_irq(info->touch_irq, info);
+ goto out_nodev;
+ }
+ enter_state_touch_detect(info);
+
+ hw_lradc_use_channel(info->x_plus_chan);
+ hw_lradc_use_channel(info->x_minus_chan);
+ hw_lradc_use_channel(info->y_plus_chan);
+ hw_lradc_use_channel(info->y_minus_chan);
+ hw_lradc_configure_channel(info->x_plus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->x_minus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->y_plus_chan, 0, 0, 0);
+ hw_lradc_configure_channel(info->y_minus_chan, 0, 0, 0);
+
+ /* Clear the accumulator & NUM_SAMPLES for the channels */
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->x_minus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_plus_chan));
+ __raw_writel(0xFFFFFFFF,
+ info->base + HW_LRADC_CHn_CLR(info->y_minus_chan));
+
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_TOUCHSCREEN,
+ 0x3c, 0, 0, 8);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_SET);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ info->base + HW_LRADC_CTRL1_SET);
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+ goto out;
+
+out_nodev:
+ input_free_device(idev);
+out_nomem:
+ kfree(info);
+out_nomem_info:
+ kfree(idev);
+out:
+ return ret;
+}
+
+static int __devexit mxs_ts_remove(struct platform_device *pdev)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ hw_lradc_unuse_channel(info->x_plus_chan);
+ hw_lradc_unuse_channel(info->x_minus_chan);
+ hw_lradc_unuse_channel(info->y_plus_chan);
+ hw_lradc_unuse_channel(info->y_minus_chan);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC0_IRQ_EN << info->y_minus_chan,
+ info->base + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ info->base + HW_LRADC_CTRL1_CLR);
+
+ free_irq(info->device_irq, info);
+ free_irq(info->touch_irq, info);
+ input_free_device(info->idev);
+
+ enter_state_disabled(info);
+ kfree(info->idev);
+ kfree(info);
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mxs_ts_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_unuse_channel(info->x_plus_chan);
+ hw_lradc_unuse_channel(info->x_minus_chan);
+ hw_lradc_unuse_channel(info->y_plus_chan);
+ hw_lradc_unuse_channel(info->y_minus_chan);
+ }
+ return 0;
+}
+
+static int mxs_ts_resume(struct platform_device *pdev)
+{
+ struct mxs_ts_info *info = platform_get_drvdata(pdev);
+
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_use_channel(info->x_plus_chan);
+ hw_lradc_use_channel(info->x_minus_chan);
+ hw_lradc_use_channel(info->y_plus_chan);
+ hw_lradc_use_channel(info->y_minus_chan);
+ }
+ return 0;
+}
+#endif
+
+static struct platform_driver mxs_ts_driver = {
+ .probe = mxs_ts_probe,
+ .remove = __devexit_p(mxs_ts_remove),
+#ifdef CONFIG_PM
+ .suspend = mxs_ts_suspend,
+ .resume = mxs_ts_resume,
+#endif
+ .driver = {
+ .name = "mxs-ts",
+ },
+};
+
+static int __init mxs_ts_init(void)
+{
+ return platform_driver_register(&mxs_ts_driver);
+}
+
+static void __exit mxs_ts_exit(void)
+{
+ platform_driver_unregister(&mxs_ts_driver);
+}
+
+module_init(mxs_ts_init);
+module_exit(mxs_ts_exit);
diff --git a/drivers/input/touchscreen/stmp3xxx_ts.c b/drivers/input/touchscreen/stmp3xxx_ts.c
new file mode 100644
index 000000000000..4e6ab20ea780
--- /dev/null
+++ b/drivers/input/touchscreen/stmp3xxx_ts.c
@@ -0,0 +1,422 @@
+/*
+ * Freesclae STMP37XX/STMP378X Touchscreen driver
+ *
+ * Author: Vitaly Wool <vital@embeddedalley.com>
+ *
+ * Copyright 2008-2009 Freescale Semiconductor, Inc. All Rights Reserved.
+ * Copyright 2008 Embedded Alley Solutions, 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
+ */
+/* #define DEBUG*/
+
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/pm.h>
+#include <linux/input.h>
+#include <linux/interrupt.h>
+
+#include <mach/lradc.h>
+#include <mach/hardware.h>
+#include <mach/platform.h>
+#include <mach/regs-lradc.h>
+
+#define TOUCH_DEBOUNCE_TOLERANCE 100
+
+struct stmp3xxx_ts_info {
+ int touch_irq;
+ int device_irq;
+ struct input_dev *idev;
+ enum {
+ TS_STATE_DISABLED,
+ TS_STATE_TOUCH_DETECT,
+ TS_STATE_TOUCH_VERIFY,
+ TS_STATE_X_PLANE,
+ TS_STATE_Y_PLANE,
+ } state;
+ u16 x;
+ u16 y;
+ int sample_count;
+};
+
+static inline void enter_state_touch_detect(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(2));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(3));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(4));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(5));
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ /*
+ * turn off the yplus and yminus pullup and pulldown, and turn off touch
+ * detect (enables yminus, and xplus through a resistor.On a press,
+ * xplus is pulled down)
+ */
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_TOUCH_DETECT;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_disabled(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 0);
+ info->state = TS_STATE_DISABLED;
+ info->sample_count = 0;
+}
+
+
+static inline void enter_state_x_plane(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+
+ info->state = TS_STATE_X_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_y_plane(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->state = TS_STATE_Y_PLANE;
+ info->sample_count = 0;
+}
+
+static inline void enter_state_touch_verify(struct stmp3xxx_ts_info *info)
+{
+ __raw_writel(BM_LRADC_CTRL0_YMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_YPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XMINUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_XPLUS_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_CLR);
+ __raw_writel(BM_LRADC_CTRL0_TOUCH_DETECT_ENABLE,
+ REGS_LRADC_BASE + HW_LRADC_CTRL0_SET);
+
+ info->state = TS_STATE_TOUCH_VERIFY;
+ hw_lradc_set_delay_trigger_kick(LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ info->sample_count = 0;
+}
+
+static void process_lradc(struct stmp3xxx_ts_info *info, u16 x, u16 y,
+ int pressure)
+{
+ switch (info->state) {
+ case TS_STATE_X_PLANE:
+ pr_debug("%s: x plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->x = x;
+ info->sample_count++;
+ } else {
+ if (abs(info->x - x) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 x_c = info->x * (info->sample_count - 1);
+ info->x = (x_c + x) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_y_plane(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_Y_PLANE:
+ pr_debug("%s: y plane state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (info->sample_count < 2) {
+ info->y = y;
+ info->sample_count++;
+ } else {
+ if (abs(info->y - y) > TOUCH_DEBOUNCE_TOLERANCE)
+ info->sample_count = 1;
+ else {
+ u16 y_c = info->y * (info->sample_count - 1);
+ info->y = (y_c + y) / info->sample_count;
+ info->sample_count++;
+ }
+ }
+ if (info->sample_count > 4)
+ enter_state_touch_verify(info);
+ else
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ break;
+
+ case TS_STATE_TOUCH_VERIFY:
+ pr_debug("%s: touch verify state, sample_count %d\n", __func__,
+ info->sample_count);
+ pr_debug("%s: x %d, y %d\n", __func__, info->x, info->y);
+ input_report_abs(info->idev, ABS_X, info->x);
+ input_report_abs(info->idev, ABS_Y, info->y);
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ input_sync(info->idev);
+ /* fall through */
+ case TS_STATE_TOUCH_DETECT:
+ pr_debug("%s: touch detect state, sample_count %d\n", __func__,
+ info->sample_count);
+ if (pressure) {
+ input_report_abs(info->idev, ABS_PRESSURE, pressure);
+ enter_state_x_plane(info);
+ hw_lradc_set_delay_trigger_kick(
+ LRADC_DELAY_TRIGGER_TOUCHSCREEN, 1);
+ } else
+ enter_state_touch_detect(info);
+ break;
+
+ default:
+ printk(KERN_ERR "%s: unknown touchscreen state %d\n", __func__,
+ info->state);
+ }
+}
+
+static irqreturn_t ts_handler(int irq, void *dev_id)
+{
+ struct stmp3xxx_ts_info *info = dev_id;
+ u16 x_plus, y_plus;
+ int pressure = 0;
+
+ if (irq == info->touch_irq)
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ else if (irq == info->device_irq)
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ /* get x, y values */
+ x_plus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_TOUCH_X_PLUS)) & BM_LRADC_CHn_VALUE;
+ y_plus = __raw_readl(REGS_LRADC_BASE + HW_LRADC_CHn(LRADC_TOUCH_Y_PLUS)) & BM_LRADC_CHn_VALUE;
+
+ /* pressed? */
+ if (__raw_readl(REGS_LRADC_BASE + HW_LRADC_STATUS) & BM_LRADC_STATUS_TOUCH_DETECT_RAW)
+ pressure = 1;
+
+ pr_debug("%s: irq %d, x_plus %d, y_plus %d, pressure %d\n",
+ __func__, irq, x_plus, y_plus, pressure);
+
+ process_lradc(info, x_plus, y_plus, pressure);
+
+ return IRQ_HANDLED;
+}
+
+static int stmp3xxx_ts_probe(struct platform_device *pdev)
+{
+ struct input_dev *idev;
+ struct stmp3xxx_ts_info *info;
+ int ret = 0;
+ struct resource *res;
+
+ idev = input_allocate_device();
+ info = kzalloc(sizeof(struct stmp3xxx_ts_info), GFP_KERNEL);
+ if (idev == NULL || info == NULL) {
+ ret = -ENOMEM;
+ goto out_nomem;
+ }
+
+ idev->name = "STMP3XXX touchscreen";
+ idev->evbit[0] = BIT(EV_ABS);
+ input_set_abs_params(idev, ABS_X, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_Y, 0, 0xFFF, 0, 0);
+ input_set_abs_params(idev, ABS_PRESSURE, 0, 1, 0, 0);
+
+ ret = input_register_device(idev);
+ if (ret)
+ goto out_nomem;
+
+ info->idev = idev;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->touch_irq = res->start;
+
+ res = platform_get_resource(pdev, IORESOURCE_IRQ, 1);
+ if (!res) {
+ printk(KERN_ERR "%s: couldn't get IRQ resource\n", __func__);
+ ret = -ENODEV;
+ goto out_nodev;
+ }
+ info->device_irq = res->start;
+
+ ret = request_irq(info->touch_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_touch", info);
+ if (ret)
+ goto out_nodev;
+
+ ret = request_irq(info->device_irq, ts_handler, IRQF_DISABLED,
+ "stmp3xxx_ts_dev", info);
+ if (ret) {
+ free_irq(info->touch_irq, info);
+ goto out_nodev;
+ }
+ enter_state_touch_detect(info);
+
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ hw_lradc_configure_channel(LRADC_CH2, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH3, 0, 0, 0);
+ hw_lradc_configure_channel(LRADC_CH5, 0, 0, 0);
+
+ /* Clear the accumulator & NUM_SAMPLES for the channels */
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH2));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH3));
+ __raw_writel(0xFFFFFFFF, REGS_LRADC_BASE + HW_LRADC_CHn_CLR(LRADC_CH5));
+
+ hw_lradc_set_delay_trigger(LRADC_DELAY_TRIGGER_TOUCHSCREEN,
+ 0x3c, 0, 0, 8);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_SET);
+
+ platform_set_drvdata(pdev, info);
+ device_init_wakeup(&pdev->dev, 1);
+ goto out;
+
+out_nodev:
+ input_free_device(idev);
+out_nomem:
+ kfree(idev);
+ kfree(info);
+out:
+ return ret;
+}
+
+static int stmp3xxx_ts_remove(struct platform_device *pdev)
+{
+ struct stmp3xxx_ts_info *info = platform_get_drvdata(pdev);
+
+ platform_set_drvdata(pdev, NULL);
+
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ __raw_writel(BM_LRADC_CTRL1_LRADC5_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+ __raw_writel(BM_LRADC_CTRL1_TOUCH_DETECT_IRQ_EN,
+ REGS_LRADC_BASE + HW_LRADC_CTRL1_CLR);
+
+ free_irq(info->device_irq, info);
+ free_irq(info->touch_irq, info);
+ input_free_device(info->idev);
+
+ enter_state_disabled(info);
+ kfree(info->idev);
+ kfree(info);
+ return 0;
+}
+
+static int stmp3xxx_ts_suspend(struct platform_device *pdev,
+ pm_message_t state)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_unuse_channel(LRADC_CH2);
+ hw_lradc_unuse_channel(LRADC_CH3);
+ hw_lradc_unuse_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static int stmp3xxx_ts_resume(struct platform_device *pdev)
+{
+#ifdef CONFIG_PM
+ if (!device_may_wakeup(&pdev->dev)) {
+ hw_lradc_use_channel(LRADC_CH2);
+ hw_lradc_use_channel(LRADC_CH3);
+ hw_lradc_use_channel(LRADC_CH5);
+ }
+#endif
+ return 0;
+}
+
+static struct platform_driver stmp3xxx_ts_driver = {
+ .probe = stmp3xxx_ts_probe,
+ .remove = stmp3xxx_ts_remove,
+ .suspend = stmp3xxx_ts_suspend,
+ .resume = stmp3xxx_ts_resume,
+ .driver = {
+ .name = "stmp3xxx_ts",
+ },
+};
+
+static int __init stmp3xxx_ts_init(void)
+{
+ return platform_driver_register(&stmp3xxx_ts_driver);
+}
+
+static void __exit stmp3xxx_ts_exit(void)
+{
+ platform_driver_unregister(&stmp3xxx_ts_driver);
+}
+
+module_init(stmp3xxx_ts_init);
+module_exit(stmp3xxx_ts_exit);
diff --git a/drivers/input/touchscreen/tsc2007.c b/drivers/input/touchscreen/tsc2007.c
index 880f58c6a7c4..fd5a503ffb93 100644
--- a/drivers/input/touchscreen/tsc2007.c
+++ b/drivers/input/touchscreen/tsc2007.c
@@ -21,15 +21,13 @@
*/
#include <linux/module.h>
-#include <linux/hrtimer.h>
#include <linux/slab.h>
#include <linux/input.h>
#include <linux/interrupt.h>
#include <linux/i2c.h>
#include <linux/i2c/tsc2007.h>
-#define TS_POLL_DELAY (10 * 1000) /* ns delay before the first sample */
-#define TS_POLL_PERIOD (5 * 1000) /* ns delay between samples */
+#define TS_POLL_PERIOD msecs_to_jiffies(1) /* ms delay between samples */
#define TSC2007_MEASURE_TEMP0 (0x0 << 4)
#define TSC2007_MEASURE_AUX (0x2 << 4)
@@ -70,13 +68,11 @@ struct ts_event {
struct tsc2007 {
struct input_dev *input;
char phys[32];
- struct hrtimer timer;
+ struct delayed_work work;
struct ts_event tc;
struct i2c_client *client;
- spinlock_t lock;
-
u16 model;
u16 x_plate_ohms;
@@ -142,8 +138,7 @@ static void tsc2007_send_event(void *tsc)
if (rt > MAX_12BIT) {
dev_dbg(&ts->client->dev, "ignored pressure %d\n", rt);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
return;
}
@@ -153,7 +148,7 @@ static void tsc2007_send_event(void *tsc)
* in some cases may not even settle at the expected value.
*
* The only safe way to check for the pen up condition is in the
- * timer by reading the pen signal state (it's a GPIO _and_ IRQ).
+ * work function by reading the pen signal state (it's a GPIO and IRQ).
*/
if (rt) {
struct input_dev *input = ts->input;
@@ -175,8 +170,7 @@ static void tsc2007_send_event(void *tsc)
x, y, rt);
}
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_PERIOD),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, TS_POLL_PERIOD);
}
static int tsc2007_read_values(struct tsc2007 *tsc)
@@ -197,13 +191,10 @@ static int tsc2007_read_values(struct tsc2007 *tsc)
return 0;
}
-static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
+static void tsc2007_work(struct work_struct *work)
{
- struct tsc2007 *ts = container_of(handle, struct tsc2007, timer);
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
-
+ struct tsc2007 *ts =
+ container_of(to_delayed_work(work), struct tsc2007, work);
if (unlikely(!ts->get_pendown_state() && ts->pendown)) {
struct input_dev *input = ts->input;
@@ -222,30 +213,20 @@ static enum hrtimer_restart tsc2007_timer(struct hrtimer *handle)
tsc2007_read_values(ts);
tsc2007_send_event(ts);
}
-
- spin_unlock_irqrestore(&ts->lock, flags);
-
- return HRTIMER_NORESTART;
}
static irqreturn_t tsc2007_irq(int irq, void *handle)
{
struct tsc2007 *ts = handle;
- unsigned long flags;
-
- spin_lock_irqsave(&ts->lock, flags);
if (likely(ts->get_pendown_state())) {
disable_irq_nosync(ts->irq);
- hrtimer_start(&ts->timer, ktime_set(0, TS_POLL_DELAY),
- HRTIMER_MODE_REL);
+ schedule_delayed_work(&ts->work, 0);
}
if (ts->clear_penirq)
ts->clear_penirq();
- spin_unlock_irqrestore(&ts->lock, flags);
-
return IRQ_HANDLED;
}
@@ -278,11 +259,6 @@ static int tsc2007_probe(struct i2c_client *client,
ts->input = input_dev;
- hrtimer_init(&ts->timer, CLOCK_MONOTONIC, HRTIMER_MODE_REL);
- ts->timer.function = tsc2007_timer;
-
- spin_lock_init(&ts->lock);
-
ts->model = pdata->model;
ts->x_plate_ohms = pdata->x_plate_ohms;
ts->get_pendown_state = pdata->get_pendown_state;
@@ -290,6 +266,11 @@ static int tsc2007_probe(struct i2c_client *client,
pdata->init_platform_hw();
+ if (tsc2007_xfer(ts, PWRDOWN) < 0) {
+ err = -ENODEV;
+ goto err_no_dev;
+ }
+
snprintf(ts->phys, sizeof(ts->phys),
"%s/input0", dev_name(&client->dev));
@@ -308,6 +289,8 @@ static int tsc2007_probe(struct i2c_client *client,
ts->irq = client->irq;
+ INIT_DELAYED_WORK(&ts->work, tsc2007_work);
+
err = request_irq(ts->irq, tsc2007_irq, 0,
client->dev.driver->name, ts);
if (err < 0) {
@@ -325,9 +308,10 @@ static int tsc2007_probe(struct i2c_client *client,
err_free_irq:
free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
err_free_mem:
input_free_device(input_dev);
+ err_no_dev:
+ pdata->exit_platform_hw();
kfree(ts);
return err;
}
@@ -337,11 +321,12 @@ static int tsc2007_remove(struct i2c_client *client)
struct tsc2007 *ts = i2c_get_clientdata(client);
struct tsc2007_platform_data *pdata;
+ cancel_delayed_work_sync(&ts->work);
+
pdata = client->dev.platform_data;
pdata->exit_platform_hw();
free_irq(ts->irq, ts);
- hrtimer_cancel(&ts->timer);
input_unregister_device(ts->input);
kfree(ts);