1*5a729246SThomas Gleixner // SPDX-License-Identifier: GPL-2.0-only 2adff5962SNishanth Menon /* 3adff5962SNishanth Menon * Texas Instruments' Palmas Power Button Input Driver 4adff5962SNishanth Menon * 5adff5962SNishanth Menon * Copyright (C) 2012-2014 Texas Instruments Incorporated - http://www.ti.com/ 6adff5962SNishanth Menon * Girish S Ghongdemath 7adff5962SNishanth Menon * Nishanth Menon 8adff5962SNishanth Menon */ 9adff5962SNishanth Menon 10b7b2b49eSGeert Uytterhoeven #include <linux/bitfield.h> 11adff5962SNishanth Menon #include <linux/init.h> 12adff5962SNishanth Menon #include <linux/input.h> 13adff5962SNishanth Menon #include <linux/interrupt.h> 14adff5962SNishanth Menon #include <linux/kernel.h> 15adff5962SNishanth Menon #include <linux/mfd/palmas.h> 16adff5962SNishanth Menon #include <linux/module.h> 17adff5962SNishanth Menon #include <linux/of.h> 18adff5962SNishanth Menon #include <linux/platform_device.h> 19adff5962SNishanth Menon #include <linux/slab.h> 20adff5962SNishanth Menon 21adff5962SNishanth Menon #define PALMAS_LPK_TIME_MASK 0x0c 22adff5962SNishanth Menon #define PALMAS_PWRON_DEBOUNCE_MASK 0x03 23adff5962SNishanth Menon #define PALMAS_PWR_KEY_Q_TIME_MS 20 24adff5962SNishanth Menon 25adff5962SNishanth Menon /** 26adff5962SNishanth Menon * struct palmas_pwron - Palmas power on data 27adff5962SNishanth Menon * @palmas: pointer to palmas device 28adff5962SNishanth Menon * @input_dev: pointer to input device 29adff5962SNishanth Menon * @input_work: work for detecting release of key 30adff5962SNishanth Menon * @irq: irq that we are hooked on to 31adff5962SNishanth Menon */ 32adff5962SNishanth Menon struct palmas_pwron { 33adff5962SNishanth Menon struct palmas *palmas; 34adff5962SNishanth Menon struct input_dev *input_dev; 35adff5962SNishanth Menon struct delayed_work input_work; 36adff5962SNishanth Menon int irq; 37adff5962SNishanth Menon }; 38adff5962SNishanth Menon 39adff5962SNishanth Menon /** 40adff5962SNishanth Menon * struct palmas_pwron_config - configuration of palmas power on 41adff5962SNishanth Menon * @long_press_time_val: value for long press h/w shutdown event 42adff5962SNishanth Menon * @pwron_debounce_val: value for debounce of power button 43adff5962SNishanth Menon */ 44adff5962SNishanth Menon struct palmas_pwron_config { 45adff5962SNishanth Menon u8 long_press_time_val; 46adff5962SNishanth Menon u8 pwron_debounce_val; 47adff5962SNishanth Menon }; 48adff5962SNishanth Menon 49adff5962SNishanth Menon /** 50adff5962SNishanth Menon * palmas_power_button_work() - Detects the button release event 51adff5962SNishanth Menon * @work: work item to detect button release 52adff5962SNishanth Menon */ 53adff5962SNishanth Menon static void palmas_power_button_work(struct work_struct *work) 54adff5962SNishanth Menon { 55adff5962SNishanth Menon struct palmas_pwron *pwron = container_of(work, 56adff5962SNishanth Menon struct palmas_pwron, 57adff5962SNishanth Menon input_work.work); 58adff5962SNishanth Menon struct input_dev *input_dev = pwron->input_dev; 59adff5962SNishanth Menon unsigned int reg; 60adff5962SNishanth Menon int error; 61adff5962SNishanth Menon 62adff5962SNishanth Menon error = palmas_read(pwron->palmas, PALMAS_INTERRUPT_BASE, 63adff5962SNishanth Menon PALMAS_INT1_LINE_STATE, ®); 64adff5962SNishanth Menon if (error) { 65adff5962SNishanth Menon dev_err(input_dev->dev.parent, 66adff5962SNishanth Menon "Cannot read palmas PWRON status: %d\n", error); 67adff5962SNishanth Menon } else if (reg & BIT(1)) { 68adff5962SNishanth Menon /* The button is released, report event. */ 69adff5962SNishanth Menon input_report_key(input_dev, KEY_POWER, 0); 70adff5962SNishanth Menon input_sync(input_dev); 71adff5962SNishanth Menon } else { 72adff5962SNishanth Menon /* The button is still depressed, keep checking. */ 73adff5962SNishanth Menon schedule_delayed_work(&pwron->input_work, 74adff5962SNishanth Menon msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); 75adff5962SNishanth Menon } 76adff5962SNishanth Menon } 77adff5962SNishanth Menon 78adff5962SNishanth Menon /** 79adff5962SNishanth Menon * pwron_irq() - button press isr 80adff5962SNishanth Menon * @irq: irq 81adff5962SNishanth Menon * @palmas_pwron: pwron struct 82adff5962SNishanth Menon * 83adff5962SNishanth Menon * Return: IRQ_HANDLED 84adff5962SNishanth Menon */ 85adff5962SNishanth Menon static irqreturn_t pwron_irq(int irq, void *palmas_pwron) 86adff5962SNishanth Menon { 87adff5962SNishanth Menon struct palmas_pwron *pwron = palmas_pwron; 88adff5962SNishanth Menon struct input_dev *input_dev = pwron->input_dev; 89adff5962SNishanth Menon 90adff5962SNishanth Menon input_report_key(input_dev, KEY_POWER, 1); 91adff5962SNishanth Menon pm_wakeup_event(input_dev->dev.parent, 0); 92adff5962SNishanth Menon input_sync(input_dev); 93adff5962SNishanth Menon 94adff5962SNishanth Menon mod_delayed_work(system_wq, &pwron->input_work, 95adff5962SNishanth Menon msecs_to_jiffies(PALMAS_PWR_KEY_Q_TIME_MS)); 96adff5962SNishanth Menon 97adff5962SNishanth Menon return IRQ_HANDLED; 98adff5962SNishanth Menon } 99adff5962SNishanth Menon 100adff5962SNishanth Menon /** 101adff5962SNishanth Menon * palmas_pwron_params_ofinit() - device tree parameter parser 102adff5962SNishanth Menon * @dev: palmas button device 103adff5962SNishanth Menon * @config: configuration params that this fills up 104adff5962SNishanth Menon */ 105adff5962SNishanth Menon static void palmas_pwron_params_ofinit(struct device *dev, 106adff5962SNishanth Menon struct palmas_pwron_config *config) 107adff5962SNishanth Menon { 108adff5962SNishanth Menon struct device_node *np; 109adff5962SNishanth Menon u32 val; 110adff5962SNishanth Menon int i, error; 111b85a4d96SColin Ian King static const u8 lpk_times[] = { 6, 8, 10, 12 }; 112b85a4d96SColin Ian King static const int pwr_on_deb_ms[] = { 15, 100, 500, 1000 }; 113adff5962SNishanth Menon 114adff5962SNishanth Menon memset(config, 0, sizeof(*config)); 115adff5962SNishanth Menon 116adff5962SNishanth Menon /* Default config parameters */ 117adff5962SNishanth Menon config->long_press_time_val = ARRAY_SIZE(lpk_times) - 1; 118adff5962SNishanth Menon 119adff5962SNishanth Menon np = dev->of_node; 120adff5962SNishanth Menon if (!np) 121adff5962SNishanth Menon return; 122adff5962SNishanth Menon 123adff5962SNishanth Menon error = of_property_read_u32(np, "ti,palmas-long-press-seconds", &val); 124adff5962SNishanth Menon if (!error) { 125adff5962SNishanth Menon for (i = 0; i < ARRAY_SIZE(lpk_times); i++) { 126adff5962SNishanth Menon if (val <= lpk_times[i]) { 127adff5962SNishanth Menon config->long_press_time_val = i; 128adff5962SNishanth Menon break; 129adff5962SNishanth Menon } 130adff5962SNishanth Menon } 131adff5962SNishanth Menon } 132adff5962SNishanth Menon 133adff5962SNishanth Menon error = of_property_read_u32(np, 134adff5962SNishanth Menon "ti,palmas-pwron-debounce-milli-seconds", 135adff5962SNishanth Menon &val); 136adff5962SNishanth Menon if (!error) { 137adff5962SNishanth Menon for (i = 0; i < ARRAY_SIZE(pwr_on_deb_ms); i++) { 138adff5962SNishanth Menon if (val <= pwr_on_deb_ms[i]) { 139adff5962SNishanth Menon config->pwron_debounce_val = i; 140adff5962SNishanth Menon break; 141adff5962SNishanth Menon } 142adff5962SNishanth Menon } 143adff5962SNishanth Menon } 144adff5962SNishanth Menon 145adff5962SNishanth Menon dev_info(dev, "h/w controlled shutdown duration=%d seconds\n", 146adff5962SNishanth Menon lpk_times[config->long_press_time_val]); 147adff5962SNishanth Menon } 148adff5962SNishanth Menon 149adff5962SNishanth Menon /** 150adff5962SNishanth Menon * palmas_pwron_probe() - probe 151adff5962SNishanth Menon * @pdev: platform device for the button 152adff5962SNishanth Menon * 153adff5962SNishanth Menon * Return: 0 for successful probe else appropriate error 154adff5962SNishanth Menon */ 155adff5962SNishanth Menon static int palmas_pwron_probe(struct platform_device *pdev) 156adff5962SNishanth Menon { 157adff5962SNishanth Menon struct palmas *palmas = dev_get_drvdata(pdev->dev.parent); 158adff5962SNishanth Menon struct device *dev = &pdev->dev; 159adff5962SNishanth Menon struct input_dev *input_dev; 160adff5962SNishanth Menon struct palmas_pwron *pwron; 161adff5962SNishanth Menon struct palmas_pwron_config config; 162adff5962SNishanth Menon int val; 163adff5962SNishanth Menon int error; 164adff5962SNishanth Menon 165adff5962SNishanth Menon palmas_pwron_params_ofinit(dev, &config); 166adff5962SNishanth Menon 167adff5962SNishanth Menon pwron = kzalloc(sizeof(*pwron), GFP_KERNEL); 168adff5962SNishanth Menon if (!pwron) 169adff5962SNishanth Menon return -ENOMEM; 170adff5962SNishanth Menon 171adff5962SNishanth Menon input_dev = input_allocate_device(); 172adff5962SNishanth Menon if (!input_dev) { 173adff5962SNishanth Menon dev_err(dev, "Can't allocate power button\n"); 174adff5962SNishanth Menon error = -ENOMEM; 175adff5962SNishanth Menon goto err_free_mem; 176adff5962SNishanth Menon } 177adff5962SNishanth Menon 178adff5962SNishanth Menon input_dev->name = "palmas_pwron"; 179adff5962SNishanth Menon input_dev->phys = "palmas_pwron/input0"; 180adff5962SNishanth Menon input_dev->dev.parent = dev; 181adff5962SNishanth Menon 182adff5962SNishanth Menon input_set_capability(input_dev, EV_KEY, KEY_POWER); 183adff5962SNishanth Menon 184adff5962SNishanth Menon /* 185adff5962SNishanth Menon * Setup default hardware shutdown option (long key press) 186adff5962SNishanth Menon * and debounce. 187adff5962SNishanth Menon */ 188b7b2b49eSGeert Uytterhoeven val = FIELD_PREP(PALMAS_LPK_TIME_MASK, config.long_press_time_val) | 189b7b2b49eSGeert Uytterhoeven FIELD_PREP(PALMAS_PWRON_DEBOUNCE_MASK, config.pwron_debounce_val); 190adff5962SNishanth Menon error = palmas_update_bits(palmas, PALMAS_PMU_CONTROL_BASE, 191adff5962SNishanth Menon PALMAS_LONG_PRESS_KEY, 192adff5962SNishanth Menon PALMAS_LPK_TIME_MASK | 193adff5962SNishanth Menon PALMAS_PWRON_DEBOUNCE_MASK, 194adff5962SNishanth Menon val); 195adff5962SNishanth Menon if (error) { 196adff5962SNishanth Menon dev_err(dev, "LONG_PRESS_KEY_UPDATE failed: %d\n", error); 197adff5962SNishanth Menon goto err_free_input; 198adff5962SNishanth Menon } 199adff5962SNishanth Menon 200adff5962SNishanth Menon pwron->palmas = palmas; 201adff5962SNishanth Menon pwron->input_dev = input_dev; 202adff5962SNishanth Menon 203adff5962SNishanth Menon INIT_DELAYED_WORK(&pwron->input_work, palmas_power_button_work); 204adff5962SNishanth Menon 205adff5962SNishanth Menon pwron->irq = platform_get_irq(pdev, 0); 206daf87bffSArvind Yadav if (pwron->irq < 0) { 207daf87bffSArvind Yadav error = pwron->irq; 208daf87bffSArvind Yadav goto err_free_input; 209daf87bffSArvind Yadav } 210daf87bffSArvind Yadav 211adff5962SNishanth Menon error = request_threaded_irq(pwron->irq, NULL, pwron_irq, 2125cc19b7cSNishanth Menon IRQF_TRIGGER_HIGH | 2135cc19b7cSNishanth Menon IRQF_TRIGGER_LOW | 2145cc19b7cSNishanth Menon IRQF_ONESHOT, 215adff5962SNishanth Menon dev_name(dev), pwron); 216adff5962SNishanth Menon if (error) { 217adff5962SNishanth Menon dev_err(dev, "Can't get IRQ for pwron: %d\n", error); 218adff5962SNishanth Menon goto err_free_input; 219adff5962SNishanth Menon } 220adff5962SNishanth Menon 221adff5962SNishanth Menon error = input_register_device(input_dev); 222adff5962SNishanth Menon if (error) { 223adff5962SNishanth Menon dev_err(dev, "Can't register power button: %d\n", error); 224adff5962SNishanth Menon goto err_free_irq; 225adff5962SNishanth Menon } 226adff5962SNishanth Menon 227adff5962SNishanth Menon platform_set_drvdata(pdev, pwron); 228adff5962SNishanth Menon device_init_wakeup(dev, true); 229adff5962SNishanth Menon 230adff5962SNishanth Menon return 0; 231adff5962SNishanth Menon 232adff5962SNishanth Menon err_free_irq: 233adff5962SNishanth Menon cancel_delayed_work_sync(&pwron->input_work); 234adff5962SNishanth Menon free_irq(pwron->irq, pwron); 235adff5962SNishanth Menon err_free_input: 236adff5962SNishanth Menon input_free_device(input_dev); 237adff5962SNishanth Menon err_free_mem: 238adff5962SNishanth Menon kfree(pwron); 239adff5962SNishanth Menon return error; 240adff5962SNishanth Menon } 241adff5962SNishanth Menon 242adff5962SNishanth Menon /** 243adff5962SNishanth Menon * palmas_pwron_remove() - Cleanup on removal 244adff5962SNishanth Menon * @pdev: platform device for the button 245adff5962SNishanth Menon * 246adff5962SNishanth Menon * Return: 0 247adff5962SNishanth Menon */ 248adff5962SNishanth Menon static int palmas_pwron_remove(struct platform_device *pdev) 249adff5962SNishanth Menon { 250adff5962SNishanth Menon struct palmas_pwron *pwron = platform_get_drvdata(pdev); 251adff5962SNishanth Menon 252adff5962SNishanth Menon free_irq(pwron->irq, pwron); 253adff5962SNishanth Menon cancel_delayed_work_sync(&pwron->input_work); 254adff5962SNishanth Menon 255adff5962SNishanth Menon input_unregister_device(pwron->input_dev); 256adff5962SNishanth Menon kfree(pwron); 257adff5962SNishanth Menon 258adff5962SNishanth Menon return 0; 259adff5962SNishanth Menon } 260adff5962SNishanth Menon 261adff5962SNishanth Menon /** 262adff5962SNishanth Menon * palmas_pwron_suspend() - suspend handler 263adff5962SNishanth Menon * @dev: power button device 264adff5962SNishanth Menon * 265adff5962SNishanth Menon * Cancel all pending work items for the power button, setup irq for wakeup 266adff5962SNishanth Menon * 267adff5962SNishanth Menon * Return: 0 268adff5962SNishanth Menon */ 26997a652a8SJingoo Han static int __maybe_unused palmas_pwron_suspend(struct device *dev) 270adff5962SNishanth Menon { 271adff5962SNishanth Menon struct platform_device *pdev = to_platform_device(dev); 272adff5962SNishanth Menon struct palmas_pwron *pwron = platform_get_drvdata(pdev); 273adff5962SNishanth Menon 274adff5962SNishanth Menon cancel_delayed_work_sync(&pwron->input_work); 275adff5962SNishanth Menon 276adff5962SNishanth Menon if (device_may_wakeup(dev)) 277adff5962SNishanth Menon enable_irq_wake(pwron->irq); 278adff5962SNishanth Menon 279adff5962SNishanth Menon return 0; 280adff5962SNishanth Menon } 281adff5962SNishanth Menon 282adff5962SNishanth Menon /** 283adff5962SNishanth Menon * palmas_pwron_resume() - resume handler 284adff5962SNishanth Menon * @dev: power button device 285adff5962SNishanth Menon * 286adff5962SNishanth Menon * Just disable the wakeup capability of irq here. 287adff5962SNishanth Menon * 288adff5962SNishanth Menon * Return: 0 289adff5962SNishanth Menon */ 29097a652a8SJingoo Han static int __maybe_unused palmas_pwron_resume(struct device *dev) 291adff5962SNishanth Menon { 292adff5962SNishanth Menon struct platform_device *pdev = to_platform_device(dev); 293adff5962SNishanth Menon struct palmas_pwron *pwron = platform_get_drvdata(pdev); 294adff5962SNishanth Menon 295adff5962SNishanth Menon if (device_may_wakeup(dev)) 296adff5962SNishanth Menon disable_irq_wake(pwron->irq); 297adff5962SNishanth Menon 298adff5962SNishanth Menon return 0; 299adff5962SNishanth Menon } 300adff5962SNishanth Menon 301adff5962SNishanth Menon static SIMPLE_DEV_PM_OPS(palmas_pwron_pm, 302adff5962SNishanth Menon palmas_pwron_suspend, palmas_pwron_resume); 303adff5962SNishanth Menon 304adff5962SNishanth Menon #ifdef CONFIG_OF 305245165deSFabian Frederick static const struct of_device_id of_palmas_pwr_match[] = { 306adff5962SNishanth Menon { .compatible = "ti,palmas-pwrbutton" }, 307adff5962SNishanth Menon { }, 308adff5962SNishanth Menon }; 309adff5962SNishanth Menon 310adff5962SNishanth Menon MODULE_DEVICE_TABLE(of, of_palmas_pwr_match); 311adff5962SNishanth Menon #endif 312adff5962SNishanth Menon 313adff5962SNishanth Menon static struct platform_driver palmas_pwron_driver = { 314adff5962SNishanth Menon .probe = palmas_pwron_probe, 315adff5962SNishanth Menon .remove = palmas_pwron_remove, 316adff5962SNishanth Menon .driver = { 317adff5962SNishanth Menon .name = "palmas_pwrbutton", 318adff5962SNishanth Menon .of_match_table = of_match_ptr(of_palmas_pwr_match), 319adff5962SNishanth Menon .pm = &palmas_pwron_pm, 320adff5962SNishanth Menon }, 321adff5962SNishanth Menon }; 322adff5962SNishanth Menon module_platform_driver(palmas_pwron_driver); 323adff5962SNishanth Menon 324adff5962SNishanth Menon MODULE_ALIAS("platform:palmas-pwrbutton"); 325adff5962SNishanth Menon MODULE_DESCRIPTION("Palmas Power Button"); 32605f7588cSNishanth Menon MODULE_LICENSE("GPL v2"); 327adff5962SNishanth Menon MODULE_AUTHOR("Texas Instruments Inc."); 328