summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard')
-rw-r--r--drivers/input/keyboard/Kconfig37
-rw-r--r--drivers/input/keyboard/Makefile6
-rw-r--r--drivers/input/keyboard/mc9s08dz60_keyb.c248
-rw-r--r--drivers/input/keyboard/mpr084.c500
-rw-r--r--drivers/input/keyboard/mpr121.c305
-rw-r--r--drivers/input/keyboard/mxc_keyb.c1203
-rw-r--r--drivers/input/keyboard/mxc_pwrkey.c156
-rw-r--r--drivers/input/keyboard/mxs-kbd.c365
8 files changed, 2820 insertions, 0 deletions
diff --git a/drivers/input/keyboard/Kconfig b/drivers/input/keyboard/Kconfig
index 1ba25145b333..c58bb7a49e7f 100644
--- a/drivers/input/keyboard/Kconfig
+++ b/drivers/input/keyboard/Kconfig
@@ -426,4 +426,41 @@ config KEYBOARD_W90P910
To compile this driver as a module, choose M here: the
module will be called w90p910_keypad.
+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_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-
+
+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_MPR121
+ tristate "Freescale MPR121 Touch Keypad Driver"
+ depends on I2C
+ help
+ Say Y here if you have the touchkey Freescale Proximity Capacitive
+ Touch Sensor controller chip in your system.
+
+ If unsure, say N.
+
+ To compile this driver as a module, choose M here
endif
diff --git a/drivers/input/keyboard/Makefile b/drivers/input/keyboard/Makefile
index 4596d0c6f922..e3344b875d81 100644
--- a/drivers/input/keyboard/Makefile
+++ b/drivers/input/keyboard/Makefile
@@ -38,3 +38,9 @@ obj-$(CONFIG_KEYBOARD_SUNKBD) += sunkbd.o
obj-$(CONFIG_KEYBOARD_TWL4030) += twl4030_keypad.o
obj-$(CONFIG_KEYBOARD_XTKBD) += xtkbd.o
obj-$(CONFIG_KEYBOARD_W90P910) += w90p910_keypad.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_keyb.o
+obj-$(CONFIG_KEYBOARD_MXC) += mxc_pwrkey.o
+obj-$(CONFIG_KEYBOARD_MPR084) += mpr084.o
+obj-$(CONFIG_KEYBOARD_MXS) += mxs-kbd.o
+obj-$(CONFIG_KEYBOARD_MC9S08DZ60) += mc9s08dz60_keyb.o
+obj-$(CONFIG_KEYBOARD_MPR121) += mpr121.o
diff --git a/drivers/input/keyboard/mc9s08dz60_keyb.c b/drivers/input/keyboard/mc9s08dz60_keyb.c
new file mode 100644
index 000000000000..87ab5d3eda0a
--- /dev/null
+++ b/drivers/input/keyboard/mc9s08dz60_keyb.c
@@ -0,0 +1,248 @@
+/*
+ * Copyright 2009-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 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..d0d5a9fb0809
--- /dev/null
+++ b/drivers/input/keyboard/mpr084.c
@@ -0,0 +1,500 @@
+/*
+ * Copyright 2008-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 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/mpr121.c b/drivers/input/keyboard/mpr121.c
new file mode 100644
index 000000000000..6a723235805d
--- /dev/null
+++ b/drivers/input/keyboard/mpr121.c
@@ -0,0 +1,305 @@
+/*
+ * Copyright (C) 2010-2011 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.
+ */
+
+/*
+ * mpr121.c - Touchkey driver for Freescale MPR121 Capacitive Touch
+ * Sensor Controllor
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/i2c/mpr.h>
+#include <linux/interrupt.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/bitops.h>
+
+struct mpr121_touchkey_data {
+ struct i2c_client *client;
+ struct input_dev *input_dev;
+ unsigned int key_val;
+ int statusbits;
+ int keycount;
+ u16 keycodes[MPR121_MAX_KEY_COUNT];
+};
+
+struct mpr121_init_register {
+ int addr;
+ u8 val;
+};
+
+static struct mpr121_init_register init_reg_table[] = {
+ {MHD_RISING_ADDR, 0x1},
+ {NHD_RISING_ADDR, 0x1},
+ {NCL_RISING_ADDR, 0x0},
+ {FDL_RISING_ADDR, 0x0},
+ {MHD_FALLING_ADDR, 0x1},
+ {NHD_FALLING_ADDR, 0x1},
+ {NCL_FALLING_ADDR, 0xff},
+ {FDL_FALLING_ADDR, 0x02},
+ {FILTER_CONF_ADDR, 0x04},
+ {AFE_CONF_ADDR, 0x0b},
+ {AUTO_CONFIG_CTRL_ADDR, 0x0b},
+};
+
+static irqreturn_t mpr_touchkey_interrupt(int irq, void *dev_id)
+{
+ struct mpr121_touchkey_data *data = dev_id;
+ struct i2c_client *client = data->client;
+ struct input_dev *input = data->input_dev;
+ unsigned int key_num, pressed;
+ int reg;
+
+ reg = i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_1_ADDR);
+ if (reg < 0) {
+ dev_err(&client->dev, "i2c read error [%d]\n", reg);
+ goto out;
+ }
+
+ reg <<= 8;
+ reg |= i2c_smbus_read_byte_data(client, ELE_TOUCH_STATUS_0_ADDR);
+ if (reg < 0) {
+ dev_err(&client->dev, "i2c read error [%d]\n", reg);
+ goto out;
+ }
+
+ reg &= TOUCH_STATUS_MASK;
+ /* use old press bit to figure out which bit changed */
+ key_num = ffs(reg ^ data->statusbits) - 1;
+ /* use the bit check the press status */
+ pressed = (reg & (1 << (key_num))) >> key_num;
+ data->statusbits = reg;
+ data->key_val = data->keycodes[key_num];
+
+ input_report_key(input, data->key_val, pressed);
+ input_sync(input);
+
+ dev_dbg(&client->dev, "key %d %d %s\n", key_num, data->key_val,
+ pressed ? "pressed" : "released");
+
+out:
+ return IRQ_HANDLED;
+}
+
+static int mpr121_phys_init(struct mpr121_platform_data *pdata,
+ struct mpr121_touchkey_data *data,
+ struct i2c_client *client)
+{
+ struct mpr121_init_register *reg;
+ unsigned char usl, lsl, tl;
+ int i, t, vdd, ret;
+
+ /* setup touch/release threshold for ele0-ele11 */
+ for (i = 0; i <= MPR121_MAX_KEY_COUNT; i++) {
+ t = ELE0_TOUCH_THRESHOLD_ADDR + (i * 2);
+ ret = i2c_smbus_write_byte_data(client, t, TOUCH_THRESHOLD);
+ if (ret < 0)
+ goto err_i2c_write;
+ ret = i2c_smbus_write_byte_data(client, t + 1,
+ RELEASE_THRESHOLD);
+ if (ret < 0)
+ goto err_i2c_write;
+ }
+ /* setup init register */
+ for (i = 0; i < ARRAY_SIZE(init_reg_table); i++) {
+ reg = &init_reg_table[i];
+ ret = i2c_smbus_write_byte_data(client, reg->addr, reg->val);
+ if (ret < 0)
+ goto err_i2c_write;
+ }
+ /* setup auto-register by vdd,the formula please ref:AN3889 */
+ vdd = pdata->vdd_uv / 1000;
+ usl = ((vdd - 700) * 256) / vdd;
+ lsl = (usl * 65) / 100;
+ tl = (usl * 90) / 100;
+ ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_USL_ADDR, usl);
+ if (ret < 0)
+ goto err_i2c_write;
+ ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_LSL_ADDR, lsl);
+ if (ret < 0)
+ goto err_i2c_write;
+ ret = i2c_smbus_write_byte_data(client, AUTO_CONFIG_TL_ADDR, tl);
+ if (ret < 0)
+ goto err_i2c_write;
+ ret = i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR,
+ data->keycount);
+ if (ret < 0)
+ goto err_i2c_write;
+
+ dev_info(&client->dev, "mpr121: config as enable %x of electrode.\n",
+ data->keycount);
+
+ return 0;
+
+err_i2c_write:
+ dev_err(&client->dev, "i2c write error[%d]\n", ret);
+ return ret;
+}
+
+static int __devinit mpr_touchkey_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct mpr121_platform_data *pdata;
+ struct mpr121_touchkey_data *data;
+ struct input_dev *input_dev;
+ int error;
+ int i;
+
+ pdata = client->dev.platform_data;
+ if (!pdata) {
+ dev_err(&client->dev, "no platform data defined\n");
+ return -EINVAL;
+ }
+
+ data = kzalloc(sizeof(struct mpr121_touchkey_data), GFP_KERNEL);
+ input_dev = input_allocate_device();
+ if (!data || !input_dev) {
+ dev_err(&client->dev, "Falied to allocate memory\n");
+ error = -ENOMEM;
+ goto err_free_mem;
+ }
+
+ data->client = client;
+ data->input_dev = input_dev;
+ data->keycount = pdata->keycount;
+
+ if (data->keycount > MPR121_MAX_KEY_COUNT) {
+ dev_err(&client->dev, "Too many key defined\n");
+ error = -EINVAL;
+ goto err_free_mem;
+ }
+
+ error = mpr121_phys_init(pdata, data, client);
+ if (error < 0) {
+ dev_err(&client->dev, "Failed to init register\n");
+ goto err_free_mem;
+ }
+
+ i2c_set_clientdata(client, data);
+
+ input_dev->name = "FSL MPR121 Touchkey";
+ input_dev->id.bustype = BUS_I2C;
+ input_dev->dev.parent = &client->dev;
+ input_dev->keycode = pdata->matrix;
+ input_dev->keycodesize = sizeof(pdata->matrix[0]);
+ input_dev->keycodemax = data->keycount;
+
+ for (i = 0; i < input_dev->keycodemax; i++) {
+ __set_bit(pdata->matrix[i], input_dev->keybit);
+ data->keycodes[i] = pdata->matrix[i];
+ input_set_capability(input_dev, EV_KEY, pdata->matrix[i]);
+ }
+ input_set_drvdata(input_dev, data);
+
+ error = request_threaded_irq(client->irq, NULL,
+ mpr_touchkey_interrupt,
+ IRQF_TRIGGER_FALLING,
+ client->dev.driver->name, data);
+ if (error) {
+ dev_err(&client->dev, "Failed to register interrupt\n");
+ goto err_free_mem;
+ }
+
+ error = input_register_device(input_dev);
+ if (error)
+ goto err_free_irq;
+ i2c_set_clientdata(client, data);
+ device_init_wakeup(&client->dev, pdata->wakeup);
+ dev_info(&client->dev, "Mpr121 touch keyboard init success.\n");
+ return 0;
+
+err_free_irq:
+ free_irq(client->irq, data);
+err_free_mem:
+ input_free_device(input_dev);
+ kfree(data);
+ return error;
+}
+
+static int __devexit mpr_touchkey_remove(struct i2c_client *client)
+{
+ struct mpr121_touchkey_data *data = i2c_get_clientdata(client);
+
+ free_irq(client->irq, data);
+ input_unregister_device(data->input_dev);
+ kfree(data);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int mpr_suspend(struct i2c_client *client, pm_message_t mesg)
+{
+ if (device_may_wakeup(&client->dev))
+ enable_irq_wake(client->irq);
+
+ i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, 0x00);
+
+ return 0;
+}
+
+static int mpr_resume(struct i2c_client *client)
+{
+ struct mpr121_touchkey_data *data = i2c_get_clientdata(client);
+
+ if (device_may_wakeup(&client->dev))
+ disable_irq_wake(client->irq);
+
+ i2c_smbus_write_byte_data(client, ELECTRODE_CONF_ADDR, data->keycount);
+
+ return 0;
+}
+#else
+static int mpr_suspend(struct i2c_client *client, pm_message_t mesg) {}
+static int mpr_resume(struct i2c_client *client) {}
+#endif
+
+static const struct i2c_device_id mpr121_id[] = {
+ {"mpr121_touchkey", 0},
+ { }
+};
+
+static struct i2c_driver mpr_touchkey_driver = {
+ .driver = {
+ .name = "mpr121",
+ .owner = THIS_MODULE,
+ },
+ .id_table = mpr121_id,
+ .probe = mpr_touchkey_probe,
+ .remove = __devexit_p(mpr_touchkey_remove),
+ .suspend = mpr_suspend,
+ .resume = mpr_resume,
+};
+
+static int __init mpr_touchkey_init(void)
+{
+ return i2c_add_driver(&mpr_touchkey_driver);
+}
+
+static void __exit mpr_touchkey_exit(void)
+{
+ i2c_del_driver(&mpr_touchkey_driver);
+}
+
+module_init(mpr_touchkey_init);
+module_exit(mpr_touchkey_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Touch Key driver for FSL MPR121 Chip");
diff --git a/drivers/input/keyboard/mxc_keyb.c b/drivers/input/keyboard/mxc_keyb.c
new file mode 100644
index 000000000000..740140f3f0e2
--- /dev/null
+++ b/drivers/input/keyboard/mxc_keyb.c
@@ -0,0 +1,1203 @@
+/*
+ * 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 <linux/io.h>
+#include <linux/uaccess.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/fsl_devices.h>
+#include <linux/slab.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;
+
+/*! 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, flag;
+ 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",
+ irq, 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/mxc_pwrkey.c b/drivers/input/keyboard/mxc_pwrkey.c
new file mode 100644
index 000000000000..7e8ae02c8fee
--- /dev/null
+++ b/drivers/input/keyboard/mxc_pwrkey.c
@@ -0,0 +1,156 @@
+/*
+ * Copyright (C) 2010-2011 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.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+#include <linux/module.h>
+#include <linux/sched.h>
+#include <linux/slab.h>
+#include <linux/mm.h>
+#include <linux/init.h>
+#include <asm/io.h>
+#include <asm/uaccess.h>
+#include <linux/kd.h>
+#include <linux/fs.h>
+#include <linux/ioctl.h>
+#include <linux/input.h>
+#include <linux/irq.h>
+#include <linux/interrupt.h>
+#include <linux/platform_device.h>
+#include <linux/powerkey.h>
+
+struct mxc_pwrkey_priv {
+
+ struct input_dev *input;
+ int value;
+ int (*get_status) (int);
+};
+
+static struct mxc_pwrkey_priv *mxc_pwrkey;
+
+static void pwrkey_event_handler(void *param)
+{
+ int pressed;
+
+ pressed = mxc_pwrkey->get_status((int)param);
+
+ if (pressed) {
+ input_report_key(
+ mxc_pwrkey->input, mxc_pwrkey->value, 1);
+ pr_info("%s_Keydown\n", __func__);
+ } else {
+ input_report_key(
+ mxc_pwrkey->input, mxc_pwrkey->value, 0);
+ pr_info("%s_Keyup\n", __func__);
+ }
+}
+
+static int mxcpwrkey_probe(struct platform_device *pdev)
+{
+ int retval;
+ struct input_dev *input;
+ struct power_key_platform_data *pdata = pdev->dev.platform_data;
+
+ if (mxc_pwrkey) {
+ dev_warn(&pdev->dev, "two power key??\n");
+ return -EBUSY;
+ }
+
+ if (!pdata || !pdata->get_key_status) {
+ dev_err(&pdev->dev, "can not get platform data\n");
+ return -EINVAL;
+ }
+
+ mxc_pwrkey = kmalloc(sizeof(struct mxc_pwrkey_priv), GFP_KERNEL);
+ if (!mxc_pwrkey) {
+ dev_err(&pdev->dev, "can not allocate private data");
+ return -ENOMEM;
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ dev_err(&pdev->dev, "no memory for input device\n");
+ retval = -ENOMEM;
+ goto err1;
+ }
+
+ input->name = "mxc_power_key";
+ input->phys = "mxcpwrkey/input0";
+ input->id.bustype = BUS_HOST;
+ input->evbit[0] = BIT_MASK(EV_KEY);
+
+ mxc_pwrkey->value = pdata->key_value;
+ mxc_pwrkey->get_status = pdata->get_key_status;
+ mxc_pwrkey->input = input;
+ pdata->register_pwrkey(pwrkey_event_handler);
+
+ input_set_capability(input, EV_KEY, mxc_pwrkey->value);
+
+ retval = input_register_device(input);
+ if (retval < 0) {
+ dev_err(&pdev->dev, "failed to register input device\n");
+ goto err2;
+ }
+
+ printk(KERN_INFO "PMIC powerkey probe\n");
+
+ return 0;
+
+err2:
+ input_free_device(input);
+err1:
+ kfree(mxc_pwrkey);
+ mxc_pwrkey = NULL;
+
+ return retval;
+}
+
+static int mxcpwrkey_remove(struct platform_device *pdev)
+{
+ input_unregister_device(mxc_pwrkey->input);
+ input_free_device(mxc_pwrkey->input);
+ kfree(mxc_pwrkey);
+ mxc_pwrkey = NULL;
+
+ return 0;
+}
+
+static struct platform_driver mxcpwrkey_driver = {
+ .driver = {
+ .name = "mxcpwrkey",
+ },
+ .probe = mxcpwrkey_probe,
+ .remove = mxcpwrkey_remove,
+};
+
+static int __init mxcpwrkey_init(void)
+{
+ return platform_driver_register(&mxcpwrkey_driver);
+}
+
+static void __exit mxcpwrkey_exit(void)
+{
+ platform_driver_unregister(&mxcpwrkey_driver);
+}
+
+module_init(mxcpwrkey_init);
+module_exit(mxcpwrkey_exit);
+
+
+MODULE_AUTHOR("Freescale Semiconductor");
+MODULE_DESCRIPTION("MXC board power key 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..20daee01aae4
--- /dev/null
+++ b/drivers/input/keyboard/mxs-kbd.c
@@ -0,0 +1,365 @@
+/*
+ * 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/slab.h>
+#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");