linux/drivers/input/misc/pm8941-pwrkey.c
<<
>>
Prefs
   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
  46struct 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
  57static 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
 116static 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
 133static 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
 143static 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
 153static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
 154                         pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
 155
 156static 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
 265static int pm8941_pwrkey_remove(struct platform_device *pdev)
 266{
 267        struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
 268
 269        unregister_reboot_notifier(&pwrkey->reboot_notifier);
 270
 271        return 0;
 272}
 273
 274static const struct of_device_id pm8941_pwr_key_id_table[] = {
 275        { .compatible = "qcom,pm8941-pwrkey" },
 276        { }
 277};
 278MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
 279
 280static struct platform_driver pm8941_pwrkey_driver = {
 281        .probe = pm8941_pwrkey_probe,
 282        .remove = pm8941_pwrkey_remove,
 283        .driver = {
 284                .name = "pm8941-pwrkey",
 285                .pm = &pm8941_pwr_key_pm_ops,
 286                .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
 287        },
 288};
 289module_platform_driver(pm8941_pwrkey_driver);
 290
 291MODULE_DESCRIPTION("PM8941 Power Key driver");
 292MODULE_LICENSE("GPL v2");
 293