root/drivers/input/misc/palmas-pwrbutton.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. palmas_power_button_work
  2. pwron_irq
  3. palmas_pwron_params_ofinit
  4. palmas_pwron_probe
  5. palmas_pwron_remove
  6. palmas_pwron_suspend
  7. palmas_pwron_resume

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

/* [<][>][^][v][top][bottom][index][help] */