| /* | |
| * Hisilicon PMIC powerkey driver | |
| * | |
| * Copyright (C) 2013 Hisilicon Ltd. | |
| * Copyright (C) 2015, 2016 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/reboot.h> | |
| #include <linux/kernel.h> | |
| #include <linux/module.h> | |
| #include <linux/of_irq.h> | |
| #include <linux/input.h> | |
| #include <linux/slab.h> | |
| /* the held interrupt will trigger after 4 seconds */ | |
| #define MAX_HELD_TIME (4 * MSEC_PER_SEC) | |
| static irqreturn_t hi65xx_power_press_isr(int irq, void *q) | |
| { | |
| struct input_dev *input = q; | |
| pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
| input_report_key(input, KEY_POWER, 1); | |
| input_sync(input); | |
| return IRQ_HANDLED; | |
| } | |
| static irqreturn_t hi65xx_power_release_isr(int irq, void *q) | |
| { | |
| struct input_dev *input = q; | |
| pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
| input_report_key(input, KEY_POWER, 0); | |
| input_sync(input); | |
| return IRQ_HANDLED; | |
| } | |
| static irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q) | |
| { | |
| struct input_dev *input = q; | |
| int value = test_bit(KEY_RESTART, input->key); | |
| pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); | |
| input_report_key(input, KEY_RESTART, !value); | |
| input_sync(input); | |
| return IRQ_HANDLED; | |
| } | |
| static const struct { | |
| const char *name; | |
| irqreturn_t (*handler)(int irq, void *q); | |
| } hi65xx_irq_info[] = { | |
| { "down", hi65xx_power_press_isr }, | |
| { "up", hi65xx_power_release_isr }, | |
| { "hold 4s", hi65xx_restart_toggle_isr }, | |
| }; | |
| static int hi65xx_powerkey_probe(struct platform_device *pdev) | |
| { | |
| struct device *dev = &pdev->dev; | |
| struct input_dev *input; | |
| int irq, i, error; | |
| input = devm_input_allocate_device(&pdev->dev); | |
| if (!input) { | |
| dev_err(&pdev->dev, "failed to allocate input device\n"); | |
| return -ENOMEM; | |
| } | |
| input->phys = "hisi_on/input0"; | |
| input->name = "HISI 65xx PowerOn Key"; | |
| input_set_capability(input, EV_KEY, KEY_POWER); | |
| input_set_capability(input, EV_KEY, KEY_RESTART); | |
| for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) { | |
| irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name); | |
| if (irq < 0) { | |
| error = irq; | |
| dev_err(dev, "couldn't get irq %s: %d\n", | |
| hi65xx_irq_info[i].name, error); | |
| return error; | |
| } | |
| error = devm_request_any_context_irq(dev, irq, | |
| hi65xx_irq_info[i].handler, | |
| IRQF_ONESHOT, | |
| hi65xx_irq_info[i].name, | |
| input); | |
| if (error < 0) { | |
| dev_err(dev, "couldn't request irq %s: %d\n", | |
| hi65xx_irq_info[i].name, error); | |
| return error; | |
| } | |
| } | |
| error = input_register_device(input); | |
| if (error) { | |
| dev_err(&pdev->dev, "failed to register input device: %d\n", | |
| error); | |
| return error; | |
| } | |
| device_init_wakeup(&pdev->dev, 1); | |
| return 0; | |
| } | |
| static int hi65xx_powerkey_remove(struct platform_device *pdev) | |
| { | |
| device_init_wakeup(&pdev->dev, 0); | |
| return 0; | |
| } | |
| static struct platform_driver hi65xx_powerkey_driver = { | |
| .driver = { | |
| .name = "hi65xx-powerkey", | |
| }, | |
| .probe = hi65xx_powerkey_probe, | |
| .remove = hi65xx_powerkey_remove, | |
| }; | |
| module_platform_driver(hi65xx_powerkey_driver); | |
| MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); | |
| MODULE_DESCRIPTION("Hisi PMIC Power key driver"); | |
| MODULE_LICENSE("GPL v2"); |