1 /* 2 * Hisilicon PMIC powerkey driver 3 * 4 * Copyright (C) 2013 Hisilicon Ltd. 5 * Copyright (C) 2015, 2016 Linaro Ltd. 6 * 7 * This file is subject to the terms and conditions of the GNU General 8 * Public License. See the file "COPYING" in the main directory of this 9 * archive for more details. 10 * 11 * This program is distributed in the hope that it will be useful, 12 * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 * GNU General Public License for more details. 15 */ 16 17 #include <linux/platform_device.h> 18 #include <linux/interrupt.h> 19 #include <linux/reboot.h> 20 #include <linux/kernel.h> 21 #include <linux/module.h> 22 #include <linux/of_irq.h> 23 #include <linux/input.h> 24 #include <linux/slab.h> 25 26 /* the held interrupt will trigger after 4 seconds */ 27 #define MAX_HELD_TIME (4 * MSEC_PER_SEC) 28 29 static irqreturn_t hi65xx_power_press_isr(int irq, void *q) 30 { 31 struct input_dev *input = q; 32 33 pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 34 input_report_key(input, KEY_POWER, 1); 35 input_sync(input); 36 37 return IRQ_HANDLED; 38 } 39 40 static irqreturn_t hi65xx_power_release_isr(int irq, void *q) 41 { 42 struct input_dev *input = q; 43 44 pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 45 input_report_key(input, KEY_POWER, 0); 46 input_sync(input); 47 48 return IRQ_HANDLED; 49 } 50 51 static irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q) 52 { 53 struct input_dev *input = q; 54 int value = test_bit(KEY_RESTART, input->key); 55 56 pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 57 input_report_key(input, KEY_RESTART, !value); 58 input_sync(input); 59 60 return IRQ_HANDLED; 61 } 62 63 static const struct { 64 const char *name; 65 irqreturn_t (*handler)(int irq, void *q); 66 } hi65xx_irq_info[] = { 67 { "down", hi65xx_power_press_isr }, 68 { "up", hi65xx_power_release_isr }, 69 { "hold 4s", hi65xx_restart_toggle_isr }, 70 }; 71 72 static int hi65xx_powerkey_probe(struct platform_device *pdev) 73 { 74 struct device *dev = &pdev->dev; 75 struct input_dev *input; 76 int irq, i, error; 77 78 input = devm_input_allocate_device(dev); 79 if (!input) { 80 dev_err(dev, "failed to allocate input device\n"); 81 return -ENOMEM; 82 } 83 84 input->phys = "hisi_on/input0"; 85 input->name = "HISI 65xx PowerOn Key"; 86 87 input_set_capability(input, EV_KEY, KEY_POWER); 88 input_set_capability(input, EV_KEY, KEY_RESTART); 89 90 for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) { 91 92 irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name); 93 if (irq < 0) { 94 error = irq; 95 dev_err(dev, "couldn't get irq %s: %d\n", 96 hi65xx_irq_info[i].name, error); 97 return error; 98 } 99 100 error = devm_request_any_context_irq(dev, irq, 101 hi65xx_irq_info[i].handler, 102 IRQF_ONESHOT, 103 hi65xx_irq_info[i].name, 104 input); 105 if (error < 0) { 106 dev_err(dev, "couldn't request irq %s: %d\n", 107 hi65xx_irq_info[i].name, error); 108 return error; 109 } 110 } 111 112 error = input_register_device(input); 113 if (error) { 114 dev_err(dev, "failed to register input device: %d\n", error); 115 return error; 116 } 117 118 device_init_wakeup(dev, 1); 119 120 return 0; 121 } 122 123 static struct platform_driver hi65xx_powerkey_driver = { 124 .driver = { 125 .name = "hi65xx-powerkey", 126 }, 127 .probe = hi65xx_powerkey_probe, 128 }; 129 module_platform_driver(hi65xx_powerkey_driver); 130 131 MODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); 132 MODULE_DESCRIPTION("Hisi PMIC Power key driver"); 133 MODULE_LICENSE("GPL v2"); 134