summaryrefslogtreecommitdiff
path: root/drivers/input/keyboard/sprd_eic_keys.c
diff options
context:
space:
mode:
authorYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
committerYuval Adam <_@yuv.al>2017-08-30 08:25:59 +0000
commit8e4bff4cab0ddac6060645b0715210484d02ff40 (patch)
tree3a5c3024371a04692a3a6d9974d001cdff8ebf84 /drivers/input/keyboard/sprd_eic_keys.c
Initial file dump from open source releaseHEADmaster
Diffstat (limited to 'drivers/input/keyboard/sprd_eic_keys.c')
-rw-r--r--drivers/input/keyboard/sprd_eic_keys.c467
1 files changed, 467 insertions, 0 deletions
diff --git a/drivers/input/keyboard/sprd_eic_keys.c b/drivers/input/keyboard/sprd_eic_keys.c
new file mode 100644
index 00000000..886eed3b
--- /dev/null
+++ b/drivers/input/keyboard/sprd_eic_keys.c
@@ -0,0 +1,467 @@
+/*
+ * Copyright (C) 2014 Spreadtrum Communications Inc.
+ *
+ * This software is licensed under the terms of the GNU General Public
+ * License version 2, as published by the Free Software Foundation, and
+ * may be copied, distributed, and modified under those terms.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+
+#include <linux/init.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/sched.h>
+#include <linux/pm.h>
+#include <linux/slab.h>
+#include <linux/sysctl.h>
+#include <linux/proc_fs.h>
+#include <linux/delay.h>
+#include <linux/platform_device.h>
+#include <linux/input.h>
+#include <linux/sprd_eic_keys.h>
+#include <linux/workqueue.h>
+#include <linux/gpio.h>
+#include <linux/of_platform.h>
+#include <linux/of_gpio.h>
+#include <linux/spinlock.h>
+#ifdef HOOK_POWER_KEY
+#include <linux/input-hook.h>
+#endif
+
+//==================== debug ====================
+#define ENTER \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_EIC_KEYS_DBG] func: %s line: %04d\n", __func__, __LINE__); }while(0)
+
+#define PRINT_DBG(format,x...) \
+do{ if(debug_level >= 1) printk(KERN_INFO "[SPRD_EIC_KEYS_DBG] " format, ## x); }while(0)
+
+#define PRINT_INFO(format,x...) \
+do{ printk(KERN_INFO "[SPRD_EIC_KEYS_INFO] " format, ## x); }while(0)
+
+#define PRINT_WARN(format,x...) \
+do{ printk(KERN_INFO "[SPRD_EIC_KEYS_WARN] " format, ## x); }while(0)
+
+#define PRINT_ERR(format,x...) \
+do{ printk(KERN_ERR "[SPRD_EIC_KEYS_ERR] func: %s line: %04d info: " format, __func__, __LINE__, ## x); }while(0)
+//==================== debug ====================
+
+static int debug_level = 1;
+static struct sprd_eic_keys_platform_data *this_pdata = NULL;
+
+static int sprd_eic_button_state(struct sprd_eic_keys_button *button)
+{
+ int button_state = 0;
+ int gpio_value = 0;
+
+ gpio_value = !!gpio_get_value(button->gpio);
+
+ if(1 == button->active_low) {
+ if(0 == gpio_value)
+ button_state = 1;
+ else
+ button_state = 0;
+ } else {
+ if(1 == gpio_value)
+ button_state = 1;
+ else
+ button_state = 0;
+ }
+
+ PRINT_DBG("GPIO_%d=%d, button_state=%d\n", button->gpio, gpio_value, button_state);
+
+ return button_state; //0==released, 1==pressed
+}
+
+static irqreturn_t sprd_eic_irq_handle(int irq, void *dev_id)
+{
+ struct sprd_eic_keys_button *button = (struct sprd_eic_keys_button *)dev_id;
+ static int button_state_last = 0;
+ int button_state_current = 0;
+
+ ENTER;
+ disable_irq_nosync(button->irq);
+ button_state_current = sprd_eic_button_state(button);
+
+ if (1 == button_state_current) {//pressed
+#ifdef HOOK_POWER_KEY
+ #ifdef ( CONFIG_ARCH_SCX20 || CONFIG_ARCH_SCX35LT8 )
+ if(116 == button->code)
+ #endif
+ input_report_key_hook(this_pdata->input_dev, button->code, button_state_current);
+#endif
+ input_report_key(this_pdata->input_dev, button->code, button_state_current);
+ input_sync(this_pdata->input_dev);
+ PRINT_INFO("Pressed! Key:%s ScanCode:%d value:%d\n", button->desc, button->code, button_state_current);
+
+ if(1 == button->active_low)
+ irq_set_irq_type(button->irq, IRQF_TRIGGER_HIGH);
+ else
+ irq_set_irq_type(button->irq, IRQF_TRIGGER_LOW);
+
+ } else {//released
+#ifdef HOOK_POWER_KEY
+ #ifdef ( CONFIG_ARCH_SCX20 || CONFIG_ARCH_SCX35LT8 )
+ if(116 == button->code)
+ #endif
+ input_report_key_hook(this_pdata->input_dev, button->code, button_state_current);
+#endif
+ input_report_key(this_pdata->input_dev, button->code, button_state_current);
+ input_sync(this_pdata->input_dev);
+ PRINT_INFO("Released! Key:%s ScanCode:%d value:%d\n", button->desc, button->code, button_state_current);
+
+ if(1 == button->active_low)
+ irq_set_irq_type(button->irq, IRQF_TRIGGER_LOW);
+ else
+ irq_set_irq_type(button->irq, IRQF_TRIGGER_HIGH);
+
+ }
+
+ button_state_last = button_state_current;
+ enable_irq(button->irq);
+ return IRQ_HANDLED;
+}
+
+static int sprd_eic_keys_setup_key(struct sprd_eic_keys_button *button)
+{
+ const char *desc = button->desc ? button->desc : "sprd_eic_keys";
+ unsigned long irqflags = 0;
+ int error = -1;
+
+ ENTER;
+ if (gpio_is_valid(button->gpio)) {
+
+ error = gpio_request_one(button->gpio, GPIOF_IN, desc);
+ if (error < 0) {
+ PRINT_ERR("Failed to request GPIO %d, error %d\n", button->gpio, error);
+ return error;
+ }
+
+ if (button->debounce_interval) {
+ error = gpio_set_debounce(button->gpio, button->debounce_interval * 1000);
+ if (error < 0)
+ PRINT_INFO("Failed to set debounce for GPIO %d\n", button->gpio);
+ }
+
+ button->irq = gpio_to_irq(button->gpio);
+ if (button->irq < 0) {
+ error = button->irq;
+ PRINT_ERR("Unable to get irq number for GPIO %d, error %d\n", button->gpio, error);
+ goto fail;
+ }
+ } else {
+ PRINT_ERR("No GPIO specified\n");
+ return -EINVAL;
+ }
+
+ if (button->type && button->type != EV_KEY) {
+ PRINT_ERR("Only EV_KEY allowed for IRQ buttons\n");
+ return -EINVAL;
+ }
+
+ input_set_capability(this_pdata->input_dev, button->type ?: EV_KEY, button->code);
+
+ irqflags = (1 == button->active_low) ? IRQF_TRIGGER_LOW : IRQF_TRIGGER_HIGH;
+ irqflags |= IRQF_NO_SUSPEND;
+
+ error = request_any_context_irq(button->irq, sprd_eic_irq_handle, irqflags, desc, button);
+ if (error < 0) {
+ PRINT_ERR("Unable to claim irq %d; error %d\n", button->irq, error);
+ goto fail;
+ }
+
+ return 0;
+
+fail:
+ if (gpio_is_valid(button->gpio))
+ gpio_free(button->gpio);
+
+ return error;
+}
+
+#ifdef CONFIG_OF
+static struct sprd_eic_keys_platform_data *sprd_eic_keys_get_devtree_pdata(struct device *dev)
+{
+ struct device_node *node, *pp;
+ struct sprd_eic_keys_platform_data *pdata;
+ struct sprd_eic_keys_button *button;
+ int error;
+ int nbuttons;
+ int i;
+ char *input_name = NULL;
+ int ret = -1;
+
+ node = dev->of_node;
+ if (!node) {
+ error = -ENODEV;
+ goto err_out;
+ }
+
+ nbuttons = of_get_child_count(node);
+ if (nbuttons == 0) {
+ error = -ENODEV;
+ goto err_out;
+ }
+
+ pdata = kzalloc(sizeof(*pdata) + nbuttons * (sizeof *button),
+ GFP_KERNEL);
+ if (!pdata) {
+ error = -ENOMEM;
+ goto err_out;
+ }
+
+ pdata->buttons = (struct sprd_eic_keys_button *)(pdata + 1);
+ pdata->nbuttons = nbuttons;
+
+ pdata->rep = !!of_get_property(node, "autorepeat", NULL);
+
+ ret = of_property_read_string(node, "input-name", &input_name);
+ if (0 == ret) {
+ pdata->name = input_name;
+ PRINT_INFO("input-name=\"%s\"\n", input_name);
+ }
+ else
+ PRINT_WARN("failed to get input-name\n");
+
+ i = 0;
+ for_each_child_of_node(node, pp) {
+ int gpio;
+ enum of_gpio_flags flags;
+
+ if (!of_find_property(pp, "gpios", NULL)) {
+ pdata->nbuttons--;
+ dev_warn(dev, "Found button without gpios\n");
+ continue;
+ }
+
+ gpio = of_get_gpio_flags(pp, 0, &flags);
+ if (gpio < 0) {
+ error = gpio;
+ if (error != -EPROBE_DEFER)
+ dev_err(dev,
+ "Failed to get gpio flags, error: %d\n",
+ error);
+ goto err_free_pdata;
+ }
+
+ button = &pdata->buttons[i++];
+
+ button->gpio = gpio;
+ button->active_low = flags & OF_GPIO_ACTIVE_LOW;
+
+ if (of_property_read_u32(pp, "linux,code", &button->code)) {
+ dev_err(dev, "Button without keycode: 0x%x\n",
+ button->gpio);
+ error = -EINVAL;
+ goto err_free_pdata;
+ }
+
+ button->desc = of_get_property(pp, "label", NULL);
+
+ if (of_property_read_u32(pp, "linux,input-type", &button->type))
+ button->type = EV_KEY;
+
+ button->wakeup = !!of_get_property(pp, "gpio-key,wakeup", NULL);
+
+ if (of_property_read_u32(pp, "debounce-interval",
+ &button->debounce_interval))
+ button->debounce_interval = 5;
+ }
+
+ if (pdata->nbuttons == 0) {
+ error = -EINVAL;
+ goto err_free_pdata;
+ }
+
+ return pdata;
+
+err_free_pdata:
+ kfree(pdata);
+err_out:
+ return ERR_PTR(error);
+}
+
+static struct of_device_id sprd_eic_keys_of_match[] = {
+ { .compatible = "sprd,sprd-eic-keys", },
+ { },
+};
+MODULE_DEVICE_TABLE(of, sprd_eic_keys_of_match);
+
+#else
+
+static inline struct sprd_eic_keys_platform_data *sprd_eic_keys_get_devtree_pdata(struct device *dev)
+{
+ return ERR_PTR(-ENODEV);
+}
+
+#endif
+static ssize_t sprd_eic_button_show(struct kobject *kobj, struct kobj_attribute *attr, char *buff)
+{
+ int status = 0;
+
+ status = sprd_eic_button_state(&this_pdata->buttons[0]);
+ PRINT_INFO("button status = %d (0 == released, 1 == pressed)\n", status);
+ return sprintf(buff, "%d\n", status);
+}
+
+static struct kobject *sprd_eic_button_kobj = NULL;
+static struct kobj_attribute sprd_eic_button_attr =
+ __ATTR(status, 0644, sprd_eic_button_show, NULL);
+
+static int sprd_eic_button_sysfs_init(void)
+{
+ int ret = -1;
+
+ sprd_eic_button_kobj = kobject_create_and_add("sprd_eic_button", kernel_kobj);
+ if (sprd_eic_button_kobj == NULL) {
+ ret = -ENOMEM;
+ PRINT_ERR("register sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ ret = sysfs_create_file(sprd_eic_button_kobj, &sprd_eic_button_attr.attr);
+ if (ret) {
+ PRINT_ERR("create sysfs failed. ret = %d\n", ret);
+ return ret;
+ }
+
+ PRINT_INFO("sprd_eic_button_sysfs_init success\n");
+ return ret;
+}
+static int sprd_eic_keys_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct sprd_eic_keys_button *button = NULL;
+ struct input_dev *input = NULL;
+ int i = 0;
+ int error = -1;
+
+ ENTER;
+ this_pdata = dev_get_platdata(dev);
+
+ if (!this_pdata) {
+ this_pdata = sprd_eic_keys_get_devtree_pdata(dev);
+ if (IS_ERR(this_pdata)) {
+ PRINT_ERR("failed to get platform data!\n");
+ return PTR_ERR(this_pdata);
+ }
+ }
+
+ input = input_allocate_device();
+ if (!input) {
+ PRINT_ERR("failed to allocate input device!\n");
+ error = -ENOMEM;
+ goto fail1;
+ }
+
+ input->name = this_pdata->name ? this_pdata->name : pdev->name;
+ input->id.bustype = BUS_HOST;
+ this_pdata->input_dev = input;
+
+ if (this_pdata->rep)
+ __set_bit(EV_REP, input->evbit);
+
+ for (i = 0; i < this_pdata->nbuttons; i++) {
+ button = &this_pdata->buttons[i];
+ error = sprd_eic_keys_setup_key( button);
+ if (error)
+ goto fail2;
+ }
+
+ error = input_register_device(input);
+ if (error) {
+ PRINT_ERR("Unable to register input device!\n");
+ goto fail3;
+ }
+ sprd_eic_button_sysfs_init();
+ PRINT_INFO("probe success!\n");
+ return 0;
+
+fail3:
+fail2:
+ while (--i >= 0) {
+ button = &this_pdata->buttons[i];
+ free_irq(button->irq, button);
+ if (gpio_is_valid(button->gpio)) {
+ gpio_free(button->gpio);
+ }
+ }
+
+ input_free_device(input);
+fail1:
+ PRINT_ERR("probe failed!\n");
+ return error;
+}
+
+#ifdef CONFIG_PM
+static int sprd_eic_keys_suspend(struct platform_device *dev, pm_message_t state)
+{
+ return 0;
+}
+
+static int sprd_eic_keys_resume(struct platform_device *dev)
+{
+ return 0;
+}
+#else
+#define sprd_eic_keys_suspend NULL
+#define sprd_eic_keys_resume NULL
+#endif
+
+static struct platform_driver sprd_eic_keys_device_driver = {
+ .probe = sprd_eic_keys_probe,
+ .suspend = NULL,
+ .resume = NULL,
+ .driver = {
+ .name = "sprd-eic-keys",
+ .owner = THIS_MODULE,
+ .of_match_table = of_match_ptr(sprd_eic_keys_of_match),
+ }
+};
+
+static int __init sprd_eic_keys_init(void)
+{
+ ENTER;
+#ifdef HOOK_POWER_KEY
+ input_hook_init();
+#endif
+
+/***************************************************************/
+ struct device_node *node = of_find_compatible_node(NULL, NULL, "sprd,sprd-eic-keys");
+ if(node) {
+ PRINT_INFO("Find the node name:%s\n",node->name);
+
+ struct platform_device *dev = of_find_device_by_node(node);
+ if(dev)
+ PRINT_INFO("Find the device name:%s\n",dev->name);
+ else
+ PRINT_INFO("Not Find the device\n");
+ }
+ else
+ PRINT_INFO("Not Find the node\n");
+/***************************************************************/
+
+ return platform_driver_register(&sprd_eic_keys_device_driver);
+}
+
+static void __exit sprd_eic_keys_exit(void)
+{
+#ifdef HOOK_POWER_KEY
+ input_hook_exit();
+#endif
+ platform_driver_unregister(&sprd_eic_keys_device_driver);
+}
+
+late_initcall(sprd_eic_keys_init);
+module_exit(sprd_eic_keys_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Yaochuan Li <yaochuan.li@spreadtrum.com>");
+MODULE_DESCRIPTION("Keyboard driver for spreadtrum eic keys");
+MODULE_ALIAS("platform:sprd-eic-keys");