162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Hisilicon PMIC powerkey driver 362306a36Sopenharmony_ci * 462306a36Sopenharmony_ci * Copyright (C) 2013 Hisilicon Ltd. 562306a36Sopenharmony_ci * Copyright (C) 2015, 2016 Linaro Ltd. 662306a36Sopenharmony_ci * 762306a36Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General 862306a36Sopenharmony_ci * Public License. See the file "COPYING" in the main directory of this 962306a36Sopenharmony_ci * archive for more details. 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * This program is distributed in the hope that it will be useful, 1262306a36Sopenharmony_ci * but WITHOUT ANY WARRANTY; without even the implied warranty of 1362306a36Sopenharmony_ci * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 1462306a36Sopenharmony_ci * GNU General Public License for more details. 1562306a36Sopenharmony_ci */ 1662306a36Sopenharmony_ci 1762306a36Sopenharmony_ci#include <linux/platform_device.h> 1862306a36Sopenharmony_ci#include <linux/interrupt.h> 1962306a36Sopenharmony_ci#include <linux/reboot.h> 2062306a36Sopenharmony_ci#include <linux/kernel.h> 2162306a36Sopenharmony_ci#include <linux/module.h> 2262306a36Sopenharmony_ci#include <linux/of_irq.h> 2362306a36Sopenharmony_ci#include <linux/input.h> 2462306a36Sopenharmony_ci#include <linux/slab.h> 2562306a36Sopenharmony_ci 2662306a36Sopenharmony_ci/* the held interrupt will trigger after 4 seconds */ 2762306a36Sopenharmony_ci#define MAX_HELD_TIME (4 * MSEC_PER_SEC) 2862306a36Sopenharmony_ci 2962306a36Sopenharmony_cistatic irqreturn_t hi65xx_power_press_isr(int irq, void *q) 3062306a36Sopenharmony_ci{ 3162306a36Sopenharmony_ci struct input_dev *input = q; 3262306a36Sopenharmony_ci 3362306a36Sopenharmony_ci pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 3462306a36Sopenharmony_ci input_report_key(input, KEY_POWER, 1); 3562306a36Sopenharmony_ci input_sync(input); 3662306a36Sopenharmony_ci 3762306a36Sopenharmony_ci return IRQ_HANDLED; 3862306a36Sopenharmony_ci} 3962306a36Sopenharmony_ci 4062306a36Sopenharmony_cistatic irqreturn_t hi65xx_power_release_isr(int irq, void *q) 4162306a36Sopenharmony_ci{ 4262306a36Sopenharmony_ci struct input_dev *input = q; 4362306a36Sopenharmony_ci 4462306a36Sopenharmony_ci pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 4562306a36Sopenharmony_ci input_report_key(input, KEY_POWER, 0); 4662306a36Sopenharmony_ci input_sync(input); 4762306a36Sopenharmony_ci 4862306a36Sopenharmony_ci return IRQ_HANDLED; 4962306a36Sopenharmony_ci} 5062306a36Sopenharmony_ci 5162306a36Sopenharmony_cistatic irqreturn_t hi65xx_restart_toggle_isr(int irq, void *q) 5262306a36Sopenharmony_ci{ 5362306a36Sopenharmony_ci struct input_dev *input = q; 5462306a36Sopenharmony_ci int value = test_bit(KEY_RESTART, input->key); 5562306a36Sopenharmony_ci 5662306a36Sopenharmony_ci pm_wakeup_event(input->dev.parent, MAX_HELD_TIME); 5762306a36Sopenharmony_ci input_report_key(input, KEY_RESTART, !value); 5862306a36Sopenharmony_ci input_sync(input); 5962306a36Sopenharmony_ci 6062306a36Sopenharmony_ci return IRQ_HANDLED; 6162306a36Sopenharmony_ci} 6262306a36Sopenharmony_ci 6362306a36Sopenharmony_cistatic const struct { 6462306a36Sopenharmony_ci const char *name; 6562306a36Sopenharmony_ci irqreturn_t (*handler)(int irq, void *q); 6662306a36Sopenharmony_ci} hi65xx_irq_info[] = { 6762306a36Sopenharmony_ci { "down", hi65xx_power_press_isr }, 6862306a36Sopenharmony_ci { "up", hi65xx_power_release_isr }, 6962306a36Sopenharmony_ci { "hold 4s", hi65xx_restart_toggle_isr }, 7062306a36Sopenharmony_ci}; 7162306a36Sopenharmony_ci 7262306a36Sopenharmony_cistatic int hi65xx_powerkey_probe(struct platform_device *pdev) 7362306a36Sopenharmony_ci{ 7462306a36Sopenharmony_ci struct device *dev = &pdev->dev; 7562306a36Sopenharmony_ci struct input_dev *input; 7662306a36Sopenharmony_ci int irq, i, error; 7762306a36Sopenharmony_ci 7862306a36Sopenharmony_ci input = devm_input_allocate_device(dev); 7962306a36Sopenharmony_ci if (!input) { 8062306a36Sopenharmony_ci dev_err(dev, "failed to allocate input device\n"); 8162306a36Sopenharmony_ci return -ENOMEM; 8262306a36Sopenharmony_ci } 8362306a36Sopenharmony_ci 8462306a36Sopenharmony_ci input->phys = "hisi_on/input0"; 8562306a36Sopenharmony_ci input->name = "HISI 65xx PowerOn Key"; 8662306a36Sopenharmony_ci 8762306a36Sopenharmony_ci input_set_capability(input, EV_KEY, KEY_POWER); 8862306a36Sopenharmony_ci input_set_capability(input, EV_KEY, KEY_RESTART); 8962306a36Sopenharmony_ci 9062306a36Sopenharmony_ci for (i = 0; i < ARRAY_SIZE(hi65xx_irq_info); i++) { 9162306a36Sopenharmony_ci 9262306a36Sopenharmony_ci irq = platform_get_irq_byname(pdev, hi65xx_irq_info[i].name); 9362306a36Sopenharmony_ci if (irq < 0) 9462306a36Sopenharmony_ci return irq; 9562306a36Sopenharmony_ci 9662306a36Sopenharmony_ci error = devm_request_any_context_irq(dev, irq, 9762306a36Sopenharmony_ci hi65xx_irq_info[i].handler, 9862306a36Sopenharmony_ci IRQF_ONESHOT, 9962306a36Sopenharmony_ci hi65xx_irq_info[i].name, 10062306a36Sopenharmony_ci input); 10162306a36Sopenharmony_ci if (error < 0) { 10262306a36Sopenharmony_ci dev_err(dev, "couldn't request irq %s: %d\n", 10362306a36Sopenharmony_ci hi65xx_irq_info[i].name, error); 10462306a36Sopenharmony_ci return error; 10562306a36Sopenharmony_ci } 10662306a36Sopenharmony_ci } 10762306a36Sopenharmony_ci 10862306a36Sopenharmony_ci error = input_register_device(input); 10962306a36Sopenharmony_ci if (error) { 11062306a36Sopenharmony_ci dev_err(dev, "failed to register input device: %d\n", error); 11162306a36Sopenharmony_ci return error; 11262306a36Sopenharmony_ci } 11362306a36Sopenharmony_ci 11462306a36Sopenharmony_ci device_init_wakeup(dev, 1); 11562306a36Sopenharmony_ci 11662306a36Sopenharmony_ci return 0; 11762306a36Sopenharmony_ci} 11862306a36Sopenharmony_ci 11962306a36Sopenharmony_cistatic struct platform_driver hi65xx_powerkey_driver = { 12062306a36Sopenharmony_ci .driver = { 12162306a36Sopenharmony_ci .name = "hi65xx-powerkey", 12262306a36Sopenharmony_ci }, 12362306a36Sopenharmony_ci .probe = hi65xx_powerkey_probe, 12462306a36Sopenharmony_ci}; 12562306a36Sopenharmony_cimodule_platform_driver(hi65xx_powerkey_driver); 12662306a36Sopenharmony_ci 12762306a36Sopenharmony_ciMODULE_AUTHOR("Zhiliang Xue <xuezhiliang@huawei.com"); 12862306a36Sopenharmony_ciMODULE_DESCRIPTION("Hisi PMIC Power key driver"); 12962306a36Sopenharmony_ciMODULE_LICENSE("GPL v2"); 130