Linux kernel mirror (for testing) git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
kernel os linux

input: Add support for Qualcomm PMIC8XXX power key

Add support for PMIC8XXX power key driven over dedicated
KYPD_PWR_N pin.

Acked-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>
Signed-off-by: Trilok Soni <tsoni@codeaurora.org>
Signed-off-by: Anirudh Ghayal <aghayal@codeaurora.org>
Signed-off-by: Samuel Ortiz <sameo@linux.intel.com>

authored by

Trilok Soni and committed by
Samuel Ortiz
92d57a73 39325b59

+274
+11
drivers/input/misc/Kconfig
··· 330 330 To compile this driver as a module, choose M here: the module will be 331 331 called pwm-beeper. 332 332 333 + config INPUT_PMIC8XXX_PWRKEY 334 + tristate "PMIC8XXX power key support" 335 + depends on MFD_PM8XXX 336 + help 337 + Say Y here if you want support for the PMIC8XXX power key. 338 + 339 + If unsure, say N. 340 + 341 + To compile this driver as a module, choose M here: the 342 + module will be called pmic8xxx-pwrkey. 343 + 333 344 config INPUT_GPIO_ROTARY_ENCODER 334 345 tristate "Rotary encoders connected to GPIO pins" 335 346 depends on GPIOLIB && GENERIC_GPIO
+1
drivers/input/misc/Makefile
··· 33 33 obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o 34 34 obj-$(CONFIG_INPUT_POWERMATE) += powermate.o 35 35 obj-$(CONFIG_INPUT_PWM_BEEPER) += pwm-beeper.o 36 + obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o 36 37 obj-$(CONFIG_INPUT_RB532_BUTTON) += rb532_button.o 37 38 obj-$(CONFIG_INPUT_GPIO_ROTARY_ENCODER) += rotary_encoder.o 38 39 obj-$(CONFIG_INPUT_SGI_BTNS) += sgi_btns.o
+231
drivers/input/misc/pmic8xxx-pwrkey.c
··· 1 + /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 2 + * 3 + * This program is free software; you can redistribute it and/or modify 4 + * it under the terms of the GNU General Public License version 2 and 5 + * only version 2 as published by the Free Software Foundation. 6 + * 7 + * This program is distributed in the hope that it will be useful, 8 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 + * GNU General Public License for more details. 11 + */ 12 + 13 + #include <linux/module.h> 14 + #include <linux/init.h> 15 + #include <linux/kernel.h> 16 + #include <linux/errno.h> 17 + #include <linux/slab.h> 18 + #include <linux/input.h> 19 + #include <linux/interrupt.h> 20 + #include <linux/platform_device.h> 21 + #include <linux/log2.h> 22 + 23 + #include <linux/mfd/pm8xxx/core.h> 24 + #include <linux/input/pmic8xxx-pwrkey.h> 25 + 26 + #define PON_CNTL_1 0x1C 27 + #define PON_CNTL_PULL_UP BIT(7) 28 + #define PON_CNTL_TRIG_DELAY_MASK (0x7) 29 + 30 + /** 31 + * struct pmic8xxx_pwrkey - pmic8xxx pwrkey information 32 + * @key_press_irq: key press irq number 33 + */ 34 + struct pmic8xxx_pwrkey { 35 + struct input_dev *pwr; 36 + int key_press_irq; 37 + }; 38 + 39 + static irqreturn_t pwrkey_press_irq(int irq, void *_pwrkey) 40 + { 41 + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; 42 + 43 + input_report_key(pwrkey->pwr, KEY_POWER, 1); 44 + input_sync(pwrkey->pwr); 45 + 46 + return IRQ_HANDLED; 47 + } 48 + 49 + static irqreturn_t pwrkey_release_irq(int irq, void *_pwrkey) 50 + { 51 + struct pmic8xxx_pwrkey *pwrkey = _pwrkey; 52 + 53 + input_report_key(pwrkey->pwr, KEY_POWER, 0); 54 + input_sync(pwrkey->pwr); 55 + 56 + return IRQ_HANDLED; 57 + } 58 + 59 + #ifdef CONFIG_PM_SLEEP 60 + static int pmic8xxx_pwrkey_suspend(struct device *dev) 61 + { 62 + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); 63 + 64 + if (device_may_wakeup(dev)) 65 + enable_irq_wake(pwrkey->key_press_irq); 66 + 67 + return 0; 68 + } 69 + 70 + static int pmic8xxx_pwrkey_resume(struct device *dev) 71 + { 72 + struct pmic8xxx_pwrkey *pwrkey = dev_get_drvdata(dev); 73 + 74 + if (device_may_wakeup(dev)) 75 + disable_irq_wake(pwrkey->key_press_irq); 76 + 77 + return 0; 78 + } 79 + #endif 80 + 81 + static SIMPLE_DEV_PM_OPS(pm8xxx_pwr_key_pm_ops, 82 + pmic8xxx_pwrkey_suspend, pmic8xxx_pwrkey_resume); 83 + 84 + static int __devinit pmic8xxx_pwrkey_probe(struct platform_device *pdev) 85 + { 86 + struct input_dev *pwr; 87 + int key_release_irq = platform_get_irq(pdev, 0); 88 + int key_press_irq = platform_get_irq(pdev, 1); 89 + int err; 90 + unsigned int delay; 91 + u8 pon_cntl; 92 + struct pmic8xxx_pwrkey *pwrkey; 93 + const struct pm8xxx_pwrkey_platform_data *pdata = mfd_get_data(pdev); 94 + 95 + if (!pdata) { 96 + dev_err(&pdev->dev, "power key platform data not supplied\n"); 97 + return -EINVAL; 98 + } 99 + 100 + if (pdata->kpd_trigger_delay_us > 62500) { 101 + dev_err(&pdev->dev, "invalid power key trigger delay\n"); 102 + return -EINVAL; 103 + } 104 + 105 + pwrkey = kzalloc(sizeof(*pwrkey), GFP_KERNEL); 106 + if (!pwrkey) 107 + return -ENOMEM; 108 + 109 + pwr = input_allocate_device(); 110 + if (!pwr) { 111 + dev_dbg(&pdev->dev, "Can't allocate power button\n"); 112 + err = -ENOMEM; 113 + goto free_pwrkey; 114 + } 115 + 116 + input_set_capability(pwr, EV_KEY, KEY_POWER); 117 + 118 + pwr->name = "pmic8xxx_pwrkey"; 119 + pwr->phys = "pmic8xxx_pwrkey/input0"; 120 + pwr->dev.parent = &pdev->dev; 121 + 122 + delay = (pdata->kpd_trigger_delay_us << 10) / USEC_PER_SEC; 123 + delay = 1 + ilog2(delay); 124 + 125 + err = pm8xxx_readb(pdev->dev.parent, PON_CNTL_1, &pon_cntl); 126 + if (err < 0) { 127 + dev_err(&pdev->dev, "failed reading PON_CNTL_1 err=%d\n", err); 128 + goto free_input_dev; 129 + } 130 + 131 + pon_cntl &= ~PON_CNTL_TRIG_DELAY_MASK; 132 + pon_cntl |= (delay & PON_CNTL_TRIG_DELAY_MASK); 133 + if (pdata->pull_up) 134 + pon_cntl |= PON_CNTL_PULL_UP; 135 + else 136 + pon_cntl &= ~PON_CNTL_PULL_UP; 137 + 138 + err = pm8xxx_writeb(pdev->dev.parent, PON_CNTL_1, pon_cntl); 139 + if (err < 0) { 140 + dev_err(&pdev->dev, "failed writing PON_CNTL_1 err=%d\n", err); 141 + goto free_input_dev; 142 + } 143 + 144 + err = input_register_device(pwr); 145 + if (err) { 146 + dev_dbg(&pdev->dev, "Can't register power key: %d\n", err); 147 + goto free_input_dev; 148 + } 149 + 150 + pwrkey->key_press_irq = key_press_irq; 151 + pwrkey->pwr = pwr; 152 + 153 + platform_set_drvdata(pdev, pwrkey); 154 + 155 + err = request_irq(key_press_irq, pwrkey_press_irq, 156 + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_press", pwrkey); 157 + if (err < 0) { 158 + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", 159 + key_press_irq, err); 160 + goto unreg_input_dev; 161 + } 162 + 163 + err = request_irq(key_release_irq, pwrkey_release_irq, 164 + IRQF_TRIGGER_RISING, "pmic8xxx_pwrkey_release", pwrkey); 165 + if (err < 0) { 166 + dev_dbg(&pdev->dev, "Can't get %d IRQ for pwrkey: %d\n", 167 + key_release_irq, err); 168 + 169 + goto free_press_irq; 170 + } 171 + 172 + device_init_wakeup(&pdev->dev, pdata->wakeup); 173 + 174 + return 0; 175 + 176 + free_press_irq: 177 + free_irq(key_press_irq, NULL); 178 + unreg_input_dev: 179 + platform_set_drvdata(pdev, NULL); 180 + input_unregister_device(pwr); 181 + pwr = NULL; 182 + free_input_dev: 183 + input_free_device(pwr); 184 + free_pwrkey: 185 + kfree(pwrkey); 186 + return err; 187 + } 188 + 189 + static int __devexit pmic8xxx_pwrkey_remove(struct platform_device *pdev) 190 + { 191 + struct pmic8xxx_pwrkey *pwrkey = platform_get_drvdata(pdev); 192 + int key_release_irq = platform_get_irq(pdev, 0); 193 + int key_press_irq = platform_get_irq(pdev, 1); 194 + 195 + device_init_wakeup(&pdev->dev, 0); 196 + 197 + free_irq(key_press_irq, pwrkey); 198 + free_irq(key_release_irq, pwrkey); 199 + input_unregister_device(pwrkey->pwr); 200 + platform_set_drvdata(pdev, NULL); 201 + kfree(pwrkey); 202 + 203 + return 0; 204 + } 205 + 206 + static struct platform_driver pmic8xxx_pwrkey_driver = { 207 + .probe = pmic8xxx_pwrkey_probe, 208 + .remove = __devexit_p(pmic8xxx_pwrkey_remove), 209 + .driver = { 210 + .name = PM8XXX_PWRKEY_DEV_NAME, 211 + .owner = THIS_MODULE, 212 + .pm = &pm8xxx_pwr_key_pm_ops, 213 + }, 214 + }; 215 + 216 + static int __init pmic8xxx_pwrkey_init(void) 217 + { 218 + return platform_driver_register(&pmic8xxx_pwrkey_driver); 219 + } 220 + module_init(pmic8xxx_pwrkey_init); 221 + 222 + static void __exit pmic8xxx_pwrkey_exit(void) 223 + { 224 + platform_driver_unregister(&pmic8xxx_pwrkey_driver); 225 + } 226 + module_exit(pmic8xxx_pwrkey_exit); 227 + 228 + MODULE_ALIAS("platform:pmic8xxx_pwrkey"); 229 + MODULE_DESCRIPTION("PMIC8XXX Power Key driver"); 230 + MODULE_LICENSE("GPL v2"); 231 + MODULE_AUTHOR("Trilok Soni <tsoni@codeaurora.org>");
+31
include/linux/input/pmic8xxx-pwrkey.h
··· 1 + /* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 2 + * 3 + * This program is free software; you can redistribute it and/or modify 4 + * it under the terms of the GNU General Public License version 2 and 5 + * only version 2 as published by the Free Software Foundation. 6 + * 7 + * This program is distributed in the hope that it will be useful, 8 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 + * GNU General Public License for more details. 11 + */ 12 + 13 + #ifndef __PMIC8XXX_PWRKEY_H__ 14 + #define __PMIC8XXX_PWRKEY_H__ 15 + 16 + #define PM8XXX_PWRKEY_DEV_NAME "pm8xxx-pwrkey" 17 + 18 + /** 19 + * struct pm8xxx_pwrkey_platform_data - platform data for pwrkey driver 20 + * @pull up: power on register control for pull up/down configuration 21 + * @kpd_trigger_delay_us: time delay for power key state change interrupt 22 + * trigger. 23 + * @wakeup: configure power key as wakeup source 24 + */ 25 + struct pm8xxx_pwrkey_platform_data { 26 + bool pull_up; 27 + u32 kpd_trigger_delay_us; 28 + u32 wakeup; 29 + }; 30 + 31 + #endif /* __PMIC8XXX_PWRKEY_H__ */