diff options
author | Jorge Ramirez-Ortiz <jro@xenomai.org> | 2015-10-29 13:41:06 -0400 |
---|---|---|
committer | Vishal Bhoj <vishal.bhoj@linaro.org> | 2015-11-23 04:25:41 +0000 |
commit | 4fffe0793a890ea787d9a572e6cf621880313168 (patch) | |
tree | 3df6ea725812f0d5a1060e01fdcb5d5c2ea68454 | |
parent | 284832f993107d8ccbae5821d3dfc395d23769ea (diff) |
drivers: input: powerkey for HISI 65xx SoC
-rw-r--r-- | arch/arm64/boot/dts/hi6220.dtsi | 12 | ||||
-rw-r--r-- | drivers/input/misc/Kconfig | 8 | ||||
-rw-r--r-- | drivers/input/misc/Makefile | 1 | ||||
-rw-r--r-- | drivers/input/misc/hisi_powerkey.c | 218 |
4 files changed, 233 insertions, 6 deletions
diff --git a/arch/arm64/boot/dts/hi6220.dtsi b/arch/arm64/boot/dts/hi6220.dtsi index 854b5ba5244..1ac7df22e6a 100644 --- a/arch/arm64/boot/dts/hi6220.dtsi +++ b/arch/arm64/boot/dts/hi6220.dtsi @@ -764,12 +764,12 @@ interrupt-controller; pmu_irq_gpio = <&gpio_pmu_irq_n>; status = "ok"; - ponkey:ponkey@b1{ - compatible = "hisilicon,hi6552-powerkey"; - interrupt-parent = <&pmic>; - interrupts = <6 0>, <5 0>, <4 0>; - interrupt-names = "down", "up", "hold 1s"; - }; + ponkey:ponkey@b1{ + compatible = "hisilicon,hi6552-powerkey"; + interrupt-parent = <&pmic>; + interrupts = <6 0>, <5 0>, <4 0>; + interrupt-names = "down", "up", "hold 4s"; + }; coul: coul@1 { compatible = "hisilicon,hi6552-coul"; interrupt-parent = <&pmic>; diff --git a/drivers/input/misc/Kconfig b/drivers/input/misc/Kconfig index 838824bc7d3..674e080d289 100644 --- a/drivers/input/misc/Kconfig +++ b/drivers/input/misc/Kconfig @@ -734,5 +734,13 @@ config INPUT_DRV2667_HAPTICS To compile this driver as a module, choose M here: the module will be called drv260x-haptics. +config HISI_POWERKEY + + tristate "Hisilicon PMIC ONKEY support" + help + Say Y to enable support for PMIC ONKEY. + + To compile this driver as a module, choose M here: the + module will be called hisi_powerkey. endif diff --git a/drivers/input/misc/Makefile b/drivers/input/misc/Makefile index f77268edd9c..b79d7c3f9aa 100644 --- a/drivers/input/misc/Makefile +++ b/drivers/input/misc/Makefile @@ -70,3 +70,4 @@ obj-$(CONFIG_INPUT_WM831X_ON) += wm831x-on.o obj-$(CONFIG_INPUT_XEN_KBDDEV_FRONTEND) += xen-kbdfront.o obj-$(CONFIG_INPUT_YEALINK) += yealink.o obj-$(CONFIG_INPUT_IDEAPAD_SLIDEBAR) += ideapad_slidebar.o +obj-$(CONFIG_HISI_POWERKEY) += hisi_powerkey.o diff --git a/drivers/input/misc/hisi_powerkey.c b/drivers/input/misc/hisi_powerkey.c new file mode 100644 index 00000000000..aaa2cc80c4f --- /dev/null +++ b/drivers/input/misc/hisi_powerkey.c @@ -0,0 +1,218 @@ +/* + * hisi_powerkey.c - Hisilicon MIC powerkey driver + * + * Copyright (C) 2013 Hisilicon Ltd. + * Copyright (C) 2015 Linaro Ltd. + * + * This file is subject to the terms and conditions of the GNU General + * Public License. See the file "COPYING" in the main directory of this + * archive for more details. + * + * 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/platform_device.h> +#include <linux/interrupt.h> +#include <linux/wakelock.h> +#include <linux/reboot.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/of_irq.h> +#include <linux/input.h> +#include <linux/slab.h> + +/* irq_info : names match the device tree */ +const char *pressed_name = "down"; +const char *held_name = "hold 4s"; +const char *released_name = "up"; + +/* the above held interrupt will trigger after 4 seconds */ +#define MAX_HELD_TIME (4 * HZ) + +typedef irqreturn_t (*hi65xx_irq_handler) (int irq, void *data); +enum { id_pressed, id_released, id_held, id_last }; +static struct hi65xx_pkey_irq_info { + hi65xx_irq_handler handler; + const char *name; + int irq; +} irq_info[id_last]; + +#define INIT_IRQINFO(x) \ +do { \ + irq_info[id_##x].handler = irq_handler_id(x); \ + irq_info[id_##x].name = x##_name; \ + irq_info[id_##x].irq = -1; \ +} while (0) + +struct hi65xx_priv { + struct wake_lock wlock; + struct input_dev *idev; +}; + +static struct key_report_pairs { + int code; + int value; +} pkey_report[id_last] = { + [id_released] = { .code = KEY_POWER, .value = 0 }, + [id_pressed] = { .code = KEY_POWER, .value = 1 }, + [id_held] = { .code = KEY_RESTART, .value = 0 }, +}; + +static inline void report_key(struct input_dev *dev, int id_action) +{ + /* + * track the state of the key held event since only ON/OFF values are + * allowed on EV_KEY types: KEY_RESTART will always toggle its value to + * guarantee that the event is passed to handlers (dispossition update). + */ + if (id_action == id_held) + pkey_report[id_held].value ^= 1; + + dev_dbg(dev->dev.parent, "received - code %d, value %d\n", + pkey_report[id_action].code, + pkey_report[id_action].value); + + input_report_key(dev, pkey_report[id_action].code, + pkey_report[id_action].value); +} + +/* irq handlers */ +#define irq_handler_id(x) button_irq_##x +#define irq_handler_definition(action) \ +static irqreturn_t button_irq_##action(int irq, void *q) \ +{ \ + struct hi65xx_priv *p = q; \ + \ + if (irq != irq_info[id_##action].irq) \ + return IRQ_NONE; \ + \ + wake_lock_timeout(&p->wlock, MAX_HELD_TIME); \ + \ + report_key(p->idev, id_##action); \ + input_sync(p->idev); \ + \ + return IRQ_HANDLED; \ +} + +/* + * this device gives us three interrupts: power button pressed, released and + * held for four seconds. + */ +irq_handler_definition(released); +irq_handler_definition(pressed); +irq_handler_definition(held); + +static int hi65xx_powerkey_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct hi65xx_priv *priv; + int irq, i, ret; + + if (NULL == pdev) { + dev_err(dev, "parameter error\n"); + return -EINVAL; + } + + /* interrupt names must match the device tree */ + INIT_IRQINFO(released); + INIT_IRQINFO(pressed); + INIT_IRQINFO(held); + + priv = devm_kzalloc(dev, sizeof(struct hi65xx_priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->idev = input_allocate_device(); + if (!priv->idev) { + dev_err(&pdev->dev, "failed to allocate input device\n"); + return -ENOENT; + } + + priv->idev->evbit[0] = BIT_MASK(EV_KEY); + priv->idev->dev.parent = &pdev->dev; + priv->idev->phys = "hisi_on/input0"; + priv->idev->name = "hisi_on"; + + __set_bit(pkey_report[id_released].code, priv->idev->keybit); + __set_bit(pkey_report[id_pressed].code, priv->idev->keybit); + __set_bit(pkey_report[id_held].code, priv->idev->keybit); + + for (i = 0; i < ARRAY_SIZE(irq_info); i++) { + + irq = platform_get_irq_byname(pdev, irq_info[i].name); + if (irq < 0) { + dev_err(dev, "couldn't get irq %s\n", irq_info[i].name); + ret = irq; + goto err_irq; + } + + ret = devm_request_irq(dev, irq, irq_info[i].handler, + IRQF_NO_SUSPEND, irq_info[i].name, priv); + if (ret < 0) { + dev_err(dev, "couldn't get irq %s\n", irq_info[i].name); + goto err_irq; + } + + irq_info[i].irq = irq; + } + + wake_lock_init(&priv->wlock, WAKE_LOCK_SUSPEND, "hisi-powerkey"); + + ret = input_register_device(priv->idev); + if (ret) { + dev_err(&pdev->dev, "failed to register input device: %d\n", + ret); + ret = -ENOENT; + goto err_register; + } + + platform_set_drvdata(pdev, priv); + + return 0; + +err_register: + wake_lock_destroy(&priv->wlock); +err_irq: + input_free_device(priv->idev); + + return ret; +} + +static int hi65xx_powerkey_remove(struct platform_device *pdev) +{ + struct hi65xx_priv *priv = platform_get_drvdata(pdev); + + wake_lock_destroy(&priv->wlock); + input_unregister_device(priv->idev); + + return 0; +} + +static struct of_device_id hi65xx_powerkey_of_match[] = { + { .compatible = "hisilicon,hi6552-powerkey", }, + {}, +}; + +MODULE_DEVICE_TABLE(of, hi65xx_powerkey_of_match); + +static struct platform_driver hi65xx_powerkey_driver = { + .driver = { + .owner = THIS_MODULE, + .name = "hi65xx-powerkey", + .of_match_table = hi65xx_powerkey_of_match, + }, + .probe = hi65xx_powerkey_probe, + .remove = hi65xx_powerkey_remove, +}; + +module_platform_driver(hi65xx_powerkey_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); +MODULE_DESCRIPTION("Hisi PMIC Power key driver"); +MODULE_LICENSE("GPL v2"); + + |