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 button->idev->dev.parent = button->dev; 81 input_set_capability(button->idev, EV_KEY, KEY_POWER); 82 83 err = devm_request_threaded_irq(&pdev->dev, irq, NULL, 84 powerbutton_irq, IRQF_ONESHOT, "cpcap_pwrbutton", button); 85 if (err < 0) { 86 dev_err(&pdev->dev, "IRQ request failed: %d\n", err); 87 return err; 88 } 89 90 err = input_register_device(button->idev); 91 if (err) { 92 dev_err(&pdev->dev, "Input register failed: %d\n", err); 93 return err; 94 } 95 96 device_init_wakeup(&pdev->dev, true); 97 98 return 0; 99 } 100 101 #ifdef CONFIG_OF 102 static const struct of_device_id cpcap_pwrbutton_dt_match_table[] = { 103 { .compatible = "motorola,cpcap-pwrbutton" }, 104 {}, 105 }; 106 MODULE_DEVICE_TABLE(of, cpcap_pwrbutton_dt_match_table); 107 #endif 108 109 static struct platform_driver cpcap_power_button_driver = { 110 .probe = cpcap_power_button_probe, 111 .driver = { 112 .name = "cpcap-pwrbutton", 113 .of_match_table = of_match_ptr(cpcap_pwrbutton_dt_match_table), 114 }, 115 }; 116 module_platform_driver(cpcap_power_button_driver); 117 118 MODULE_ALIAS("platform:cpcap-pwrbutton"); 119 MODULE_DESCRIPTION("CPCAP Power Button"); 120 MODULE_LICENSE("GPL"); 121 MODULE_AUTHOR("Sebastian Reichel <sre@kernel.org>"); 122