linux/drivers/input/misc/pm8941-pwrkey.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Copyright (c) 2010-2011, 2020-2021, The Linux Foundation. All rights reserved.
   4 * Copyright (c) 2014, Sony Mobile Communications Inc.
   5 */
   6
   7#include <linux/delay.h>
   8#include <linux/errno.h>
   9#include <linux/input.h>
  10#include <linux/interrupt.h>
  11#include <linux/kernel.h>
  12#include <linux/log2.h>
  13#include <linux/module.h>
  14#include <linux/of.h>
  15#include <linux/of_device.h>
  16#include <linux/platform_device.h>
  17#include <linux/reboot.h>
  18#include <linux/regmap.h>
  19
  20#define PON_REV2                        0x01
  21
  22#define PON_RT_STS                      0x10
  23#define  PON_KPDPWR_N_SET               BIT(0)
  24#define  PON_RESIN_N_SET                BIT(1)
  25#define  PON_GEN3_RESIN_N_SET           BIT(6)
  26#define  PON_GEN3_KPDPWR_N_SET          BIT(7)
  27
  28#define PON_PS_HOLD_RST_CTL             0x5a
  29#define PON_PS_HOLD_RST_CTL2            0x5b
  30#define  PON_PS_HOLD_ENABLE             BIT(7)
  31#define  PON_PS_HOLD_TYPE_MASK          0x0f
  32#define  PON_PS_HOLD_TYPE_SHUTDOWN      4
  33#define  PON_PS_HOLD_TYPE_HARD_RESET    7
  34
  35#define PON_PULL_CTL                    0x70
  36#define  PON_KPDPWR_PULL_UP             BIT(1)
  37#define  PON_RESIN_PULL_UP              BIT(0)
  38
  39#define PON_DBC_CTL                     0x71
  40#define  PON_DBC_DELAY_MASK             0x7
  41
  42struct pm8941_data {
  43        unsigned int    pull_up_bit;
  44        unsigned int    status_bit;
  45        bool            supports_ps_hold_poff_config;
  46        bool            supports_debounce_config;
  47        const char      *name;
  48        const char      *phys;
  49};
  50
  51struct pm8941_pwrkey {
  52        struct device *dev;
  53        int irq;
  54        u32 baseaddr;
  55        struct regmap *regmap;
  56        struct input_dev *input;
  57
  58        unsigned int revision;
  59        struct notifier_block reboot_notifier;
  60
  61        u32 code;
  62        const struct pm8941_data *data;
  63};
  64
  65static int pm8941_reboot_notify(struct notifier_block *nb,
  66                                unsigned long code, void *unused)
  67{
  68        struct pm8941_pwrkey *pwrkey = container_of(nb, struct pm8941_pwrkey,
  69                                                    reboot_notifier);
  70        unsigned int enable_reg;
  71        unsigned int reset_type;
  72        int error;
  73
  74        /* PMICs with revision 0 have the enable bit in same register as ctrl */
  75        if (pwrkey->revision == 0)
  76                enable_reg = PON_PS_HOLD_RST_CTL;
  77        else
  78                enable_reg = PON_PS_HOLD_RST_CTL2;
  79
  80        error = regmap_update_bits(pwrkey->regmap,
  81                                   pwrkey->baseaddr + enable_reg,
  82                                   PON_PS_HOLD_ENABLE,
  83                                   0);
  84        if (error)
  85                dev_err(pwrkey->dev,
  86                        "unable to clear ps hold reset enable: %d\n",
  87                        error);
  88
  89        /*
  90         * Updates of PON_PS_HOLD_ENABLE requires 3 sleep cycles between
  91         * writes.
  92         */
  93        usleep_range(100, 1000);
  94
  95        switch (code) {
  96        case SYS_HALT:
  97        case SYS_POWER_OFF:
  98                reset_type = PON_PS_HOLD_TYPE_SHUTDOWN;
  99                break;
 100        case SYS_RESTART:
 101        default:
 102                reset_type = PON_PS_HOLD_TYPE_HARD_RESET;
 103                break;
 104        }
 105
 106        error = regmap_update_bits(pwrkey->regmap,
 107                                   pwrkey->baseaddr + PON_PS_HOLD_RST_CTL,
 108                                   PON_PS_HOLD_TYPE_MASK,
 109                                   reset_type);
 110        if (error)
 111                dev_err(pwrkey->dev, "unable to set ps hold reset type: %d\n",
 112                        error);
 113
 114        error = regmap_update_bits(pwrkey->regmap,
 115                                   pwrkey->baseaddr + enable_reg,
 116                                   PON_PS_HOLD_ENABLE,
 117                                   PON_PS_HOLD_ENABLE);
 118        if (error)
 119                dev_err(pwrkey->dev, "unable to re-set enable: %d\n", error);
 120
 121        return NOTIFY_DONE;
 122}
 123
 124static irqreturn_t pm8941_pwrkey_irq(int irq, void *_data)
 125{
 126        struct pm8941_pwrkey *pwrkey = _data;
 127        unsigned int sts;
 128        int error;
 129
 130        error = regmap_read(pwrkey->regmap,
 131                            pwrkey->baseaddr + PON_RT_STS, &sts);
 132        if (error)
 133                return IRQ_HANDLED;
 134
 135        input_report_key(pwrkey->input, pwrkey->code,
 136                         sts & pwrkey->data->status_bit);
 137        input_sync(pwrkey->input);
 138
 139        return IRQ_HANDLED;
 140}
 141
 142static int __maybe_unused pm8941_pwrkey_suspend(struct device *dev)
 143{
 144        struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
 145
 146        if (device_may_wakeup(dev))
 147                enable_irq_wake(pwrkey->irq);
 148
 149        return 0;
 150}
 151
 152static int __maybe_unused pm8941_pwrkey_resume(struct device *dev)
 153{
 154        struct pm8941_pwrkey *pwrkey = dev_get_drvdata(dev);
 155
 156        if (device_may_wakeup(dev))
 157                disable_irq_wake(pwrkey->irq);
 158
 159        return 0;
 160}
 161
 162static SIMPLE_DEV_PM_OPS(pm8941_pwr_key_pm_ops,
 163                         pm8941_pwrkey_suspend, pm8941_pwrkey_resume);
 164
 165static int pm8941_pwrkey_probe(struct platform_device *pdev)
 166{
 167        struct pm8941_pwrkey *pwrkey;
 168        bool pull_up;
 169        struct device *parent;
 170        u32 req_delay;
 171        int error;
 172
 173        if (of_property_read_u32(pdev->dev.of_node, "debounce", &req_delay))
 174                req_delay = 15625;
 175
 176        if (req_delay > 2000000 || req_delay == 0) {
 177                dev_err(&pdev->dev, "invalid debounce time: %u\n", req_delay);
 178                return -EINVAL;
 179        }
 180
 181        pull_up = of_property_read_bool(pdev->dev.of_node, "bias-pull-up");
 182
 183        pwrkey = devm_kzalloc(&pdev->dev, sizeof(*pwrkey), GFP_KERNEL);
 184        if (!pwrkey)
 185                return -ENOMEM;
 186
 187        pwrkey->dev = &pdev->dev;
 188        pwrkey->data = of_device_get_match_data(&pdev->dev);
 189
 190        parent = pdev->dev.parent;
 191        pwrkey->regmap = dev_get_regmap(parent, NULL);
 192        if (!pwrkey->regmap) {
 193                /*
 194                 * We failed to get regmap for parent. Let's see if we are
 195                 * a child of pon node and read regmap and reg from its
 196                 * parent.
 197                 */
 198                pwrkey->regmap = dev_get_regmap(parent->parent, NULL);
 199                if (!pwrkey->regmap) {
 200                        dev_err(&pdev->dev, "failed to locate regmap\n");
 201                        return -ENODEV;
 202                }
 203
 204                error = of_property_read_u32(parent->of_node,
 205                                             "reg", &pwrkey->baseaddr);
 206        } else {
 207                error = of_property_read_u32(pdev->dev.of_node, "reg",
 208                                             &pwrkey->baseaddr);
 209        }
 210        if (error)
 211                return error;
 212
 213        pwrkey->irq = platform_get_irq(pdev, 0);
 214        if (pwrkey->irq < 0)
 215                return pwrkey->irq;
 216
 217        error = regmap_read(pwrkey->regmap, pwrkey->baseaddr + PON_REV2,
 218                            &pwrkey->revision);
 219        if (error) {
 220                dev_err(&pdev->dev, "failed to set debounce: %d\n", error);
 221                return error;
 222        }
 223
 224        error = of_property_read_u32(pdev->dev.of_node, "linux,code",
 225                                     &pwrkey->code);
 226        if (error) {
 227                dev_dbg(&pdev->dev,
 228                        "no linux,code assuming power (%d)\n", error);
 229                pwrkey->code = KEY_POWER;
 230        }
 231
 232        pwrkey->input = devm_input_allocate_device(&pdev->dev);
 233        if (!pwrkey->input) {
 234                dev_dbg(&pdev->dev, "unable to allocate input device\n");
 235                return -ENOMEM;
 236        }
 237
 238        input_set_capability(pwrkey->input, EV_KEY, pwrkey->code);
 239
 240        pwrkey->input->name = pwrkey->data->name;
 241        pwrkey->input->phys = pwrkey->data->phys;
 242
 243        if (pwrkey->data->supports_debounce_config) {
 244                req_delay = (req_delay << 6) / USEC_PER_SEC;
 245                req_delay = ilog2(req_delay);
 246
 247                error = regmap_update_bits(pwrkey->regmap,
 248                                           pwrkey->baseaddr + PON_DBC_CTL,
 249                                           PON_DBC_DELAY_MASK,
 250                                           req_delay);
 251                if (error) {
 252                        dev_err(&pdev->dev, "failed to set debounce: %d\n",
 253                                error);
 254                        return error;
 255                }
 256        }
 257
 258        if (pwrkey->data->pull_up_bit) {
 259                error = regmap_update_bits(pwrkey->regmap,
 260                                           pwrkey->baseaddr + PON_PULL_CTL,
 261                                           pwrkey->data->pull_up_bit,
 262                                           pull_up ? pwrkey->data->pull_up_bit :
 263                                                     0);
 264                if (error) {
 265                        dev_err(&pdev->dev, "failed to set pull: %d\n", error);
 266                        return error;
 267                }
 268        }
 269
 270        error = devm_request_threaded_irq(&pdev->dev, pwrkey->irq,
 271                                          NULL, pm8941_pwrkey_irq,
 272                                          IRQF_ONESHOT,
 273                                          pwrkey->data->name, pwrkey);
 274        if (error) {
 275                dev_err(&pdev->dev, "failed requesting IRQ: %d\n", error);
 276                return error;
 277        }
 278
 279        error = input_register_device(pwrkey->input);
 280        if (error) {
 281                dev_err(&pdev->dev, "failed to register input device: %d\n",
 282                        error);
 283                return error;
 284        }
 285
 286        if (pwrkey->data->supports_ps_hold_poff_config) {
 287                pwrkey->reboot_notifier.notifier_call = pm8941_reboot_notify,
 288                error = register_reboot_notifier(&pwrkey->reboot_notifier);
 289                if (error) {
 290                        dev_err(&pdev->dev, "failed to register reboot notifier: %d\n",
 291                                error);
 292                        return error;
 293                }
 294        }
 295
 296        platform_set_drvdata(pdev, pwrkey);
 297        device_init_wakeup(&pdev->dev, 1);
 298
 299        return 0;
 300}
 301
 302static int pm8941_pwrkey_remove(struct platform_device *pdev)
 303{
 304        struct pm8941_pwrkey *pwrkey = platform_get_drvdata(pdev);
 305
 306        if (pwrkey->data->supports_ps_hold_poff_config)
 307                unregister_reboot_notifier(&pwrkey->reboot_notifier);
 308
 309        return 0;
 310}
 311
 312static const struct pm8941_data pwrkey_data = {
 313        .pull_up_bit = PON_KPDPWR_PULL_UP,
 314        .status_bit = PON_KPDPWR_N_SET,
 315        .name = "pm8941_pwrkey",
 316        .phys = "pm8941_pwrkey/input0",
 317        .supports_ps_hold_poff_config = true,
 318        .supports_debounce_config = true,
 319};
 320
 321static const struct pm8941_data resin_data = {
 322        .pull_up_bit = PON_RESIN_PULL_UP,
 323        .status_bit = PON_RESIN_N_SET,
 324        .name = "pm8941_resin",
 325        .phys = "pm8941_resin/input0",
 326        .supports_ps_hold_poff_config = true,
 327        .supports_debounce_config = true,
 328};
 329
 330static const struct pm8941_data pon_gen3_pwrkey_data = {
 331        .status_bit = PON_GEN3_KPDPWR_N_SET,
 332        .name = "pmic_pwrkey",
 333        .phys = "pmic_pwrkey/input0",
 334        .supports_ps_hold_poff_config = false,
 335        .supports_debounce_config = false,
 336};
 337
 338static const struct pm8941_data pon_gen3_resin_data = {
 339        .status_bit = PON_GEN3_RESIN_N_SET,
 340        .name = "pmic_resin",
 341        .phys = "pmic_resin/input0",
 342        .supports_ps_hold_poff_config = false,
 343        .supports_debounce_config = false,
 344};
 345
 346static const struct of_device_id pm8941_pwr_key_id_table[] = {
 347        { .compatible = "qcom,pm8941-pwrkey", .data = &pwrkey_data },
 348        { .compatible = "qcom,pm8941-resin", .data = &resin_data },
 349        { .compatible = "qcom,pmk8350-pwrkey", .data = &pon_gen3_pwrkey_data },
 350        { .compatible = "qcom,pmk8350-resin", .data = &pon_gen3_resin_data },
 351        { }
 352};
 353MODULE_DEVICE_TABLE(of, pm8941_pwr_key_id_table);
 354
 355static struct platform_driver pm8941_pwrkey_driver = {
 356        .probe = pm8941_pwrkey_probe,
 357        .remove = pm8941_pwrkey_remove,
 358        .driver = {
 359                .name = "pm8941-pwrkey",
 360                .pm = &pm8941_pwr_key_pm_ops,
 361                .of_match_table = of_match_ptr(pm8941_pwr_key_id_table),
 362        },
 363};
 364module_platform_driver(pm8941_pwrkey_driver);
 365
 366MODULE_DESCRIPTION("PM8941 Power Key driver");
 367MODULE_LICENSE("GPL v2");
 368
lxr.linux.no kindly hosted by Redpill Linpro AS, provider of Linux consulting and operations services since 1995.