summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard/mc9s08dz60_keyb.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/input/keyboard/mc9s08dz60_keyb.c')
-rw-r--r--drivers/input/keyboard/mc9s08dz60_keyb.c248
1 files changed, 248 insertions, 0 deletions
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");