cpcap-pwrbutton.c (3061B)
1/** 2 * CPCAP Power Button Input Driver 3 * 4 * Copyright (C) 2017 Sebastian Reichel <sre@kernel.org> 5 * 6 * This file is subject to the terms and conditions of the GNU General 7 * Public License. See the file "COPYING" in the main directory of this 8 * archive for more details. 9 * 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 */ 15 16#include <linux/module.h> 17#include <linux/init.h> 18#include <linux/kernel.h> 19#include <linux/errno.h> 20#include <linux/input.h> 21#include <linux/interrupt.h> 22#include <linux/regmap.h> 23#include <linux/of.h> 24#include <linux/platform_device.h> 25#include <linux/mfd/motorola-cpcap.h> 26 27#define CPCAP_IRQ_ON 23 28#define CPCAP_IRQ_ON_BITMASK (1 << (CPCAP_IRQ_ON % 16)) 29 30struct cpcap_power_button { 31 struct regmap *regmap; 32 struct input_dev *idev; 33 struct device *dev; 34}; 35 36static irqreturn_t powerbutton_irq(int irq, void *_button) 37{ 38 struct cpcap_power_button *button = _button; 39 int val; 40 41 val = cpcap_sense_virq(button->regmap, irq); 42 if (val < 0) { 43 dev_err(button->dev, "irq read failed: %d", val); 44 return IRQ_HANDLED; 45 } 46 47 pm_wakeup_event(button->dev, 0); 48 input_report_key(button->idev, KEY_POWER, val); 49 input_sync(button->idev); 50 51 return IRQ_HANDLED; 52} 53 54static int cpcap_power_button_probe(struct platform_device *pdev) 55{ 56 struct cpcap_power_button *button; 57 int irq; 58 int err; 59 60 irq = platform_get_irq(pdev, 0); 61 if (irq < 0) 62 return irq; 63 64 button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL); 65 if (!button) 66 return -ENOMEM; 67 68 button->idev = devm_input_allocate_device(&pdev->dev); 69 if (!button->idev) 70 return -ENOMEM; 71 72 button->regmap = dev_get_regmap(pdev->dev.parent, NULL); 73 if (!button->regmap) 74 return -ENODEV; 75 76 button->dev = &pdev->dev; 77 78 button->idev->name = "cpcap-pwrbutton"; 79 button->idev->phys = "cpcap-pwrbutton/input0"; 80 input_set_capability(button->idev, EV_KEY, KEY_POWER); 81 82 err = devm_request_threaded_irq(&pdev->dev, irq, NULL, 83 powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button); 84 if (err < 0) { 85 dev_err(&pdev->dev, "IRQ request failed: %d\n", err); 86 return err; 87 } 88 89 err = input_register_device(button->idev); 90 if (err) { 91 dev_err(&pdev->dev, "Input register failed: %d\n", err); 92 return err; 93 } 94 95 device_init_wakeup(&pdev->dev, true); 96 97 return 0; 98} 99 100#ifdef CONFIG_OF 101static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = { 102 { .compatible = "motorola,cpcap-pwrbutton" }, 103 {}, 104}; 105MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table); 106#endif 107 108static struct platform_driver cpcap_power_button_driver = { 109 .probe = cpcap_power_button_probe, 110 .driver = { 111 .name = "cpcap-pwrbutton", 112 .of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table), 113 }, 114}; 115module_platform_driver(cpcap_power_button_driver); 116 117MODULE_ALIAS("platform:cpcap-pwrbutton"); 118MODULE_DESCRIPTION("CPCAP Power Button"); 119MODULE_LICENSE("GPL"); 120MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>");