| /* | 
 |  * 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(dev); | 
 | 	if (!input) { | 
 | 		dev_err(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) | 
 | 			return irq; | 
 |  | 
 | 		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(dev, "failed to register input device: %d\n", error); | 
 | 		return error; | 
 | 	} | 
 |  | 
 | 	device_init_wakeup(dev, 1); | 
 |  | 
 | 	return 0; | 
 | } | 
 |  | 
 | static struct platform_driver hi65xx_powerkey_driver = { | 
 | 	.driver = { | 
 | 		.name = "hi65xx-powerkey", | 
 | 	}, | 
 | 	.probe = hi65xx_powerkey_probe, | 
 | }; | 
 | module_platform_driver(hi65xx_powerkey_driver); | 
 |  | 
 | MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); | 
 | MODULE_DESCRIPTION("Hisi PMIC Power key driver"); | 
 | MODULE_LICENSE("GPL v2"); |