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 30 struct cpcap_power_button { 31 struct regmap *regmap; 32 struct input_dev *idev; 33 struct device *dev; 34 }; 35 36 static 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 54 static int cpcap_power_button_probe(struct platform_device *pdev) 55 { 56 struct cpcap_power_button *button; 57 int irq = platform_get_irq(pdev, 0); 58 int err; 59 60 button = devm_kmalloc(&pdev->dev, sizeof(*button), GFP_KERNEL); 61 if (!button) 62 return -ENOMEM; 63 64 button->idev = devm_input_allocate_device(&pdev->dev); 65 if (!button->idev) 66 return -ENOMEM; 67 68 button->regmap = dev_get_regmap(pdev->dev.parent, NULL); 69 if (!button->regmap) 70 return -ENODEV; 71 72 button->dev = &pdev->dev; 73 74 button->idev->name = "cpcap-pwrbutton"; 75 button->idev->phys = "cpcap-pwrbutton/input0"; 76 button->idev->dev.parent = button->dev; 77 input_set_capability(button->idev, EV_KEY, KEY_POWER); 78 79 err = devm_request_threaded_irq(&pdev->dev, irq, NULL, 80 powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button); 81 if (err < 0) { 82 dev_err(&pdev->dev, "IRQ request failed: %d\n", err); 83 return err; 84 } 85 86 err = input_register_device(button->idev); 87 if (err) { 88 dev_err(&pdev->dev, "Input register failed: %d\n", err); 89 return err; 90 } 91 92 device_init_wakeup(&pdev->dev, true); 93 94 return 0; 95 } 96 97 #ifdef CONFIG_OF 98 static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = { 99 { .compatible = "motorola,cpcap-pwrbutton" }, 100 {}, 101 }; 102 MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table); 103 #endif 104 105 static struct platform_driver cpcap_power_button_driver = { 106 .probe = cpcap_power_button_probe, 107 .driver = { 108 .name = "cpcap-pwrbutton", 109 .of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table), 110 }, 111 }; 112 module_platform_driver(cpcap_power_button_driver); 113 114 MODULE_ALIAS("platform:cpcap-pwrbutton"); 115 MODULE_DESCRIPTION("CPCAP Power Button"); 116 MODULE_LICENSE("GPL"); 117 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); 118