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