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