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

Input: add Qualcomm PM8941 power key driver

Signed-off-by: Courtney Cavin <courtney.cavin@sonymobile.com>
Signed-off-by: Bjorn Andersson <bjorn.andersson@sonymobile.com>
Tested-by: Ivan T. Ivanov <iivanov@mm-sol.com>
Signed-off-by: Dmitry Torokhov <dmitry.torokhov@gmail.com>

authored by

Courtney Cavin and committed by
Dmitry Torokhov
68c581d5 7e174702

+349
+43
Documentation/devicetree/bindings/input/qcom,pm8941-pwrkey.txt
··· 1 + Qualcomm PM8941 PMIC Power Key 2 + 3 + PROPERTIES 4 + 5 + - compatible: 6 + Usage: required 7 + Value type: <string> 8 + Definition: must be one of: 9 + "qcom,pm8941-pwrkey" 10 + 11 + - reg: 12 + Usage: required 13 + Value type: <prop-encoded-array> 14 + Definition: base address of registers for block 15 + 16 + - interrupts: 17 + Usage: required 18 + Value type: <prop-encoded-array> 19 + Definition: key change interrupt; The format of the specifier is 20 + defined by the binding document describing the node's 21 + interrupt parent. 22 + 23 + - debounce: 24 + Usage: optional 25 + Value type: <u32> 26 + Definition: time in microseconds that key must be pressed or released 27 + for state change interrupt to trigger. 28 + 29 + - bias-pull-up: 30 + Usage: optional 31 + Value type: <empty> 32 + Definition: presence of this property indicates that the KPDPWR_N pin 33 + should be configured for pull up. 34 + 35 + EXAMPLE 36 + 37 + pwrkey@800 { 38 + compatible = "qcom,pm8941-pwrkey"; 39 + reg = <0x800>; 40 + interrupts = <0x0 0x8 0 IRQ_TYPE_EDGE_BOTH>; 41 + debounce = <15625>; 42 + bias-pull-up; 43 + };
+12
drivers/input/misc/Kconfig
··· 115 115 To compile this driver as a module, choose M here: the 116 116 module will be called pcspkr. 117 117 118 + config INPUT_PM8941_PWRKEY 119 + tristate "Qualcomm PM8941 power key support" 120 + depends on MFD_SPMI_PMIC 121 + help 122 + Say Y here if you want support for the power key usually found 123 + on boards using a Qualcomm PM8941 compatible PMIC. 124 + 125 + If unsure, say Y. 126 + 127 + To compile this driver as a module, choose M here: the module 128 + will be called pm8941-pwrkey. 129 + 118 130 config INPUT_PM8XXX_VIBRATOR 119 131 tristate "Qualcomm PM8XXX vibrator support" 120 132 depends on MFD_PM8XXX
+1
drivers/input/misc/Makefile
··· 50 50 obj-$(CONFIG_INPUT_PCF50633_PMU) += pcf50633-input.o 51 51 obj-$(CONFIG_INPUT_PCF8574) += pcf8574_keypad.o 52 52 obj-$(CONFIG_INPUT_PCSPKR) += pcspkr.o 53 + obj-$(CONFIG_INPUT_PM8941_PWRKEY) += pm8941-pwrkey.o 53 54 obj-$(CONFIG_INPUT_PM8XXX_VIBRATOR) += pm8xxx-vibrator.o 54 55 obj-$(CONFIG_INPUT_PMIC8XXX_PWRKEY) += pmic8xxx-pwrkey.o 55 56 obj-$(CONFIG_INPUT_POWERMATE) += powermate.o
+293
drivers/input/misc/pm8941-pwrkey.c
··· 1 + /* 2 + * Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved. 3 + * Copyright (c) 2014, Sony Mobile Communications Inc. 4 + * 5 + * This program is free software; you can redistribute it and/or modify 6 + * it under the terms of the GNU General Public License version 2 and 7 + * only version 2 as published by the Free Software Foundation. 8 + * 9 + * This program is distributed in the hope that it will be useful, 10 + * but WITHOUT ANY WARRANTY; without even the implied warranty of 11 + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 12 + * GNU General Public License for more details. 13 + */ 14 + 15 + #include <linux/delay.h> 16 + #include <linux/errno.h> 17 + #include <linux/input.h> 18 + #include <linux/interrupt.h> 19 + #include <linux/kernel.h> 20 + #include <linux/log2.h> 21 + #include <linux/module.h> 22 + #include <linux/of.h> 23 + #include <linux/platform_device.h> 24 + #include <linux/reboot.h> 25 + #include <linux/regmap.h> 26 + 27 + #define PON_REV2 0x01 28 + 29 + #define PON_RT_STS 0x10 30 + #define PON_KPDPWR_N_SET BIT(0) 31 + 32 + #define PON_PS_HOLD_RST_CTL 0x5a 33 + #define PON_PS_HOLD_RST_CTL2 0x5b 34 + #define PON_PS_HOLD_ENABLE BIT(7) 35 + #define PON_PS_HOLD_TYPE_MASK 0x0f 36 + #define PON_PS_HOLD_TYPE_SHUTDOWN 4 37 + #define PON_PS_HOLD_TYPE_HARD_RESET 7 38 + 39 + #define PON_PULL_CTL 0x70 40 + #define PON_KPDPWR_PULL_UP BIT(1) 41 + 42 + #define PON_DBC_CTL 0x71 43 + #define PON_DBC_DELAY_MASK 0x7 44 + 45 + 46 + struct pm8941_pwrkey { 47 + struct device *dev; 48 + int irq; 49 + u32 baseaddr; 50 + struct regmap *regmap; 51 + struct input_dev *input; 52 + 53 + unsigned int revision; 54 + struct notifier_block reboot_notifier; 55 + }; 56 + 57 + static int pm8941_reboot_notify(struct notifier_block *nb, 58 + unsigned long code, void *unused) 59 + { 60 + struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey, 61 + reboot_notifier); 62 + unsigned int enable_reg; 63 + unsigned int reset_type; 64 + int error; 65 + 66 + /* PMICs with revision 0 have the enable bit in same register as ctrl */ 67 + if (pwrkey->revision == 0) 68 + enable_reg = PON_PS_HOLD_RST_CTL; 69 + else 70 + enable_reg = PON_PS_HOLD_RST_CTL2; 71 + 72 + error = regmap_update_bits(pwrkey->regmap, 73 + pwrkey->baseaddr + enable_reg, 74 + PON_PS_HOLD_ENABLE, 75 + 0); 76 + if (error) 77 + dev_err(pwrkey->dev, 78 + "unable to clear ps hold reset enable: %d\n", 79 + error); 80 + 81 + /* 82 + * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between 83 + * writes. 84 + */ 85 + usleep_range(100, 1000); 86 + 87 + switch (code) { 88 + case SYS_HALT: 89 + case SYS_POWER_OFF: 90 + reset_type = PON_PS_HOLD_TYPE_SHUTDOWN; 91 + break; 92 + case SYS_RESTART: 93 + default: 94 + reset_type = PON_PS_HOLD_TYPE_HARD_RESET; 95 + break; 96 + }; 97 + 98 + error = regmap_update_bits(pwrkey->regmap, 99 + pwrkey->baseaddr + PON_PS_HOLD_RST_CTL, 100 + PON_PS_HOLD_TYPE_MASK, 101 + reset_type); 102 + if (error) 103 + dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n", 104 + error); 105 + 106 + error = regmap_update_bits(pwrkey->regmap, 107 + pwrkey->baseaddr + enable_reg, 108 + PON_PS_HOLD_ENABLE, 109 + PON_PS_HOLD_ENABLE); 110 + if (error) 111 + dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error); 112 + 113 + return NOTIFY_DONE; 114 + } 115 + 116 + static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data) 117 + { 118 + struct pm8941_pwrkey *pwrkey = _data; 119 + unsigned int sts; 120 + int error; 121 + 122 + error = regmap_read(pwrkey->regmap, 123 + pwrkey->baseaddr + PON_RT_STS, &sts); 124 + if (error) 125 + return IRQ_HANDLED; 126 + 127 + input_report_key(pwrkey->input, KEY_POWER, !!(sts & PON_KPDPWR_N_SET)); 128 + input_sync(pwrkey->input); 129 + 130 + return IRQ_HANDLED; 131 + } 132 + 133 + static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev) 134 + { 135 + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); 136 + 137 + if (device_may_wakeup(dev)) 138 + enable_irq_wake(pwrkey->irq); 139 + 140 + return 0; 141 + } 142 + 143 + static int __maybe_unused pm8941_pwrkey_resume(struct device *dev) 144 + { 145 + struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev); 146 + 147 + if (device_may_wakeup(dev)) 148 + disable_irq_wake(pwrkey->irq); 149 + 150 + return 0; 151 + } 152 + 153 + static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops, 154 + pm8941_pwrkey_suspend, pm8941_pwrkey_resume); 155 + 156 + static int pm8941_pwrkey_probe(struct platform_device *pdev) 157 + { 158 + struct pm8941_pwrkey *pwrkey; 159 + bool pull_up; 160 + u32 req_delay; 161 + int error; 162 + 163 + if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay)) 164 + req_delay = 15625; 165 + 166 + if (req_delay > 2000000 || req_delay == 0) { 167 + dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay); 168 + return -EINVAL; 169 + } 170 + 171 + pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up"); 172 + 173 + pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL); 174 + if (!pwrkey) 175 + return -ENOMEM; 176 + 177 + pwrkey->dev = &pdev->dev; 178 + 179 + pwrkey->regmap = dev_get_regmap(pdev->dev.parent, NULL); 180 + if (!pwrkey->regmap) { 181 + dev_err(&pdev->dev, "failed to locate regmap\n"); 182 + return -ENODEV; 183 + } 184 + 185 + pwrkey->irq = platform_get_irq(pdev, 0); 186 + if (pwrkey->irq < 0) { 187 + dev_err(&pdev->dev, "failed to get irq\n"); 188 + return pwrkey->irq; 189 + } 190 + 191 + error = of_property_read_u32(pdev->dev.of_node, "reg", 192 + &pwrkey->baseaddr); 193 + if (error) 194 + return error; 195 + 196 + error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2, 197 + &pwrkey->revision); 198 + if (error) { 199 + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); 200 + return error; 201 + } 202 + 203 + pwrkey->input = devm_input_allocate_device(&pdev->dev); 204 + if (!pwrkey->input) { 205 + dev_dbg(&pdev->dev, "unable to allocate input device\n"); 206 + return -ENOMEM; 207 + } 208 + 209 + input_set_capability(pwrkey->input, EV_KEY, KEY_POWER); 210 + 211 + pwrkey->input->name = "pm8941_pwrkey"; 212 + pwrkey->input->phys = "pm8941_pwrkey/input0"; 213 + 214 + req_delay = (req_delay << 6) / USEC_PER_SEC; 215 + req_delay = ilog2(req_delay); 216 + 217 + error = regmap_update_bits(pwrkey->regmap, 218 + pwrkey->baseaddr + PON_DBC_CTL, 219 + PON_DBC_DELAY_MASK, 220 + req_delay); 221 + if (error) { 222 + dev_err(&pdev->dev, "failed to set debounce: %d\n", error); 223 + return error; 224 + } 225 + 226 + error = regmap_update_bits(pwrkey->regmap, 227 + pwrkey->baseaddr + PON_PULL_CTL, 228 + PON_KPDPWR_PULL_UP, 229 + pull_up ? PON_KPDPWR_PULL_UP : 0); 230 + if (error) { 231 + dev_err(&pdev->dev, "failed to set pull: %d\n", error); 232 + return error; 233 + } 234 + 235 + error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq, 236 + NULL, pm8941_pwrkey_irq, 237 + IRQF_ONESHOT, 238 + "pm8941_pwrkey", pwrkey); 239 + if (error) { 240 + dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error); 241 + return error; 242 + } 243 + 244 + error = input_register_device(pwrkey->input); 245 + if (error) { 246 + dev_err(&pdev->dev, "failed to register input device: %d\n", 247 + error); 248 + return error; 249 + } 250 + 251 + pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify, 252 + error = register_reboot_notifier(&pwrkey->reboot_notifier); 253 + if (error) { 254 + dev_err(&pdev->dev, "failed to register reboot notifier: %d\n", 255 + error); 256 + return error; 257 + } 258 + 259 + platform_set_drvdata(pdev, pwrkey); 260 + device_init_wakeup(&pdev->dev, 1); 261 + 262 + return 0; 263 + } 264 + 265 + static int pm8941_pwrkey_remove(struct platform_device *pdev) 266 + { 267 + struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev); 268 + 269 + device_init_wakeup(&pdev->dev, 0); 270 + unregister_reboot_notifier(&pwrkey->reboot_notifier); 271 + 272 + return 0; 273 + } 274 + 275 + static const struct of_device_id pm8941_pwr_key_id_table[] = { 276 + { .compatible = "qcom,pm8941-pwrkey" }, 277 + { } 278 + }; 279 + MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table); 280 + 281 + static struct platform_driver pm8941_pwrkey_driver = { 282 + .probe = pm8941_pwrkey_probe, 283 + .remove = pm8941_pwrkey_remove, 284 + .driver = { 285 + .name = "pm8941-pwrkey", 286 + .pm = &pm8941_pwr_key_pm_ops, 287 + .of_match_table = of_match_ptr(pm8941_pwr_key_id_table), 288 + }, 289 + }; 290 + module_platform_driver(pm8941_pwrkey_driver); 291 + 292 + MODULE_DESCRIPTION("PM8941 Power Key driver"); 293 + MODULE_LICENSE("GPL v2");