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; 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 101 static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = { 102 { .compatible = "motorola,cpcap-pwrbutton" }, 103 {}, 104 }; 105 MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table); 106 #endif 107 108 static 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 }; 115 module_platform_driver(cpcap_power_button_driver); 116 117 MODULE_ALIAS("platform:cpcap-pwrbutton"); 118 MODULE_DESCRIPTION("CPCAP Power Button"); 119 MODULE_LICENSE("GPL"); 120 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); 121